Fix silly newline miss (bug #3555)
[asterisk/asterisk.git] / loader.c
index 757352f..911fb94 100755 (executable)
--- a/loader.c
+++ b/loader.c
@@ -3,9 +3,9 @@
  *
  * Module Loader
  * 
- * Copyright (C) 1999, Mark Spencer
+ * Copyright (C) 1999 - 2005, Digium, Inc.
  *
- * Mark Spencer <markster@linux-support.net>
+ * Mark Spencer <markster@digium.com>
  *
  * This program is free software, distributed under the terms of
  * the GNU General Public License
 #include <asterisk/manager.h>
 #include <asterisk/enum.h>
 #include <asterisk/rtp.h>
+#include <asterisk/lock.h>
+#ifdef __APPLE__
+#include <asterisk/dlfcn-compat.h>
+#else
 #include <dlfcn.h>
+#endif
 #include <asterisk/md5.h>
-#include <pthread.h>
 #include "asterisk.h"
 #include "astconf.h"
 
@@ -62,7 +66,7 @@ static int printdigest(unsigned char *d)
                strcat(buf, buf2);
        }
        strcat(buf, "\n");
-       ast_log(LOG_DEBUG, buf);
+       ast_log(LOG_DEBUG, "%s", buf);
        return 0;
 }
 
@@ -94,15 +98,17 @@ static struct loadupdate {
        struct loadupdate *next;
 } *updaters = NULL;
 
-static pthread_mutex_t modlock = AST_MUTEX_INITIALIZER;
+AST_MUTEX_DEFINE_STATIC(modlock);
+AST_MUTEX_DEFINE_STATIC(reloadlock);
 
 static struct module *module_list=NULL;
+static int modlistver = 0;
 
-int ast_unload_resource(char *resource_name, int force)
+int ast_unload_resource(const char *resource_name, int force)
 {
        struct module *m, *ml = NULL;
        int res = -1;
-       if (ast_pthread_mutex_lock(&modlock))
+       if (ast_mutex_lock(&modlock))
                ast_log(LOG_WARNING, "Failed to lock\n");
        m = module_list;
        while(m) {
@@ -112,7 +118,7 @@ int ast_unload_resource(char *resource_name, int force)
                                        ast_log(LOG_WARNING, "Warning:  Forcing removal of module %s with use count %d\n", resource_name, res);
                                else {
                                        ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name, res);
-                                       ast_pthread_mutex_unlock(&modlock);
+                                       ast_mutex_unlock(&modlock);
                                        return -1;
                                }
                        }
@@ -120,7 +126,7 @@ int ast_unload_resource(char *resource_name, int force)
                        if (res) {
                                ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
                                if (force <= AST_FORCE_FIRM) {
-                                       ast_pthread_mutex_unlock(&modlock);
+                                       ast_mutex_unlock(&modlock);
                                        return -1;
                                } else
                                        ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
@@ -131,64 +137,133 @@ int ast_unload_resource(char *resource_name, int force)
                                module_list = m->next;
                        dlclose(m->lib);
                        free(m);
+                       break;
                }
                ml = m;
                m = m->next;
        }
-       ast_pthread_mutex_unlock(&modlock);
+       modlistver = rand();
+       ast_mutex_unlock(&modlock);
        ast_update_use_count();
        return res;
 }
 
-void ast_module_reload(void)
+char *ast_module_helper(char *line, char *word, int pos, int state, int rpos, int needsreload)
 {
        struct module *m;
+       int which=0;
+       char *ret;
+       if (pos != rpos)
+               return NULL;
+       ast_mutex_lock(&modlock);
+       m = module_list;
+       while(m) {
+               if (!strncasecmp(word, m->resource, strlen(word)) && (m->reload || !needsreload)) {
+                       if (++which > state)
+                               break;
+               }
+               m = m->next;
+       }
+       if (m) {
+               ret = strdup(m->resource);
+       } else {
+               ret = NULL;
+               if (!strncasecmp(word, "extconfig", strlen(word))) {
+                       if (++which > state)
+                               ret = strdup("extconfig");
+               } else if (!strncasecmp(word, "manager", strlen(word))) {
+                       if (++which > state)
+                               ret = strdup("manager");
+               } else if (!strncasecmp(word, "enum", strlen(word))) {
+                       if (++which > state)
+                               ret = strdup("enum");
+               } else if (!strncasecmp(word, "rtp", strlen(word))) {
+                       if (++which > state)
+                               ret = strdup("rtp");
+               }
+                       
+       }
+       ast_mutex_unlock(&modlock);
+       return ret;
+}
 
+int ast_module_reload(const char *name)
+{
+       struct module *m;
+       int reloaded = 0;
+       int oldversion;
+       int (*reload)(void);
        /* We'll do the logger and manager the favor of calling its reload here first */
-       reload_manager();
-       ast_enum_reload();
-       ast_rtp_reload();
+
+       if (ast_mutex_trylock(&reloadlock)) {
+               ast_verbose("The previous reload command didn't finish yet\n");
+               return -1;
+       }
+       if (!name || !strcasecmp(name, "extconfig")) {
+               read_config_maps();
+               reloaded = 2;
+       }
+       if (!name || !strcasecmp(name, "manager")) {
+               reload_manager();
+               reloaded = 2;
+       }
+       if (!name || !strcasecmp(name, "enum")) {
+               ast_enum_reload();
+               reloaded = 2;
+       }
+       if (!name || !strcasecmp(name, "rtp")) {
+               ast_rtp_reload();
+               reloaded = 2;
+       }
        time(&ast_lastreloadtime);
 
-       ast_pthread_mutex_lock(&modlock);
+       ast_mutex_lock(&modlock);
+       oldversion = modlistver;
        m = module_list;
        while(m) {
-               if (m->reload) {
-                       if (option_verbose > 2) 
-                               ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", m->resource, m->description());
-                       m->reload();
+               if (!name || !strcasecmp(name, m->resource)) {
+                       if (reloaded < 1)
+                               reloaded = 1;
+                       reload = m->reload;
+                       ast_mutex_unlock(&modlock);
+                       if (reload) {
+                               reloaded = 2;
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", m->resource, m->description());
+                               reload();
+                       }
+                       ast_mutex_lock(&modlock);
+                       if (oldversion != modlistver)
+                               break;
                }
                m = m->next;
        }
-       ast_pthread_mutex_unlock(&modlock);
+       ast_mutex_unlock(&modlock);
+       ast_mutex_unlock(&reloadlock);
+       return reloaded;
 }
 
-int ast_load_resource(char *resource_name)
+static int __load_resource(const char *resource_name, const struct ast_config *cfg)
 {
        static char fn[256];
        int errors=0;
        int res;
        struct module *m;
        int flags=RTLD_NOW;
+#ifdef RTLD_GLOBAL
        char *val;
+#endif
        char *key;
-       int o;
-       struct ast_config *cfg;
        char tmp[80];
-       /* Keep the module file parsing silent */
-       o = option_verbose;
+
        if (strncasecmp(resource_name, "res_", 4)) {
-               option_verbose = 0;
-               cfg = ast_load(AST_MODULE_CONFIG);
-               option_verbose = o;
-               if (cfg) {
 #ifdef RTLD_GLOBAL
+               if (cfg) {
                        if ((val = ast_variable_retrieve(cfg, "global", resource_name))
                                        && ast_true(val))
                                flags |= RTLD_GLOBAL;
-#endif
-                       ast_destroy(cfg);
                }
+#endif
        } else {
                /* Resource modules are always loaded global and lazy */
 #ifdef RTLD_GLOBAL
@@ -198,13 +273,13 @@ int ast_load_resource(char *resource_name)
 #endif
        }
        
-       if (ast_pthread_mutex_lock(&modlock))
+       if (ast_mutex_lock(&modlock))
                ast_log(LOG_WARNING, "Failed to lock\n");
        m = module_list;
        while(m) {
                if (!strcasecmp(m->resource, resource_name)) {
                        ast_log(LOG_WARNING, "Module '%s' already exists\n", resource_name);
-                       ast_pthread_mutex_unlock(&modlock);
+                       ast_mutex_unlock(&modlock);
                        return -1;
                }
                m = m->next;
@@ -212,7 +287,7 @@ int ast_load_resource(char *resource_name)
        m = malloc(sizeof(struct module));      
        if (!m) {
                ast_log(LOG_WARNING, "Out of memory\n");
-               ast_pthread_mutex_unlock(&modlock);
+               ast_mutex_unlock(&modlock);
                return -1;
        }
        strncpy(m->resource, resource_name, sizeof(m->resource)-1);
@@ -225,7 +300,7 @@ int ast_load_resource(char *resource_name)
        if (!m->lib) {
                ast_log(LOG_WARNING, "%s\n", dlerror());
                free(m);
-               ast_pthread_mutex_unlock(&modlock);
+               ast_mutex_unlock(&modlock);
                return -1;
        }
        m->load_module = dlsym(m->lib, "load_module");
@@ -266,11 +341,11 @@ int ast_load_resource(char *resource_name)
        m->reload = dlsym(m->lib, "reload");
        if (m->reload == NULL)
                m->reload = dlsym(m->lib, "_reload");
-       if (m->key && !(key = m->key())) {
+       if (!m->key || !(key = m->key())) {
                ast_log(LOG_WARNING, "Key routine returned NULL in module %s\n", fn);
-               errors++;
-       } else
                key = NULL;
+               errors++;
+       }
        if (key && verify_key(key)) {
                ast_log(LOG_WARNING, "Unexpected key returned by module %s\n", fn);
                errors++;
@@ -279,7 +354,7 @@ int ast_load_resource(char *resource_name)
                ast_log(LOG_WARNING, "%d error(s) loading module %s, aborted\n", errors, fn);
                dlclose(m->lib);
                free(m);
-               ast_pthread_mutex_unlock(&modlock);
+               ast_mutex_unlock(&modlock);
                return -1;
        }
        if (!fully_booted) {
@@ -291,10 +366,24 @@ int ast_load_resource(char *resource_name)
                if (option_verbose)
                        ast_verbose(VERBOSE_PREFIX_1 "Loaded %s => (%s)\n", fn, m->description());
        }
-       m->next = module_list;
+
+       /* add module 'm' to end of module_list chain
+          so reload commands will be issued in same order modules were loaded */
+       m->next = NULL;
+       if (module_list == NULL) {
+               /* empty list so far, add at front */
+               module_list = m;
+       }
+       else {
+               struct module *i;
+               /* find end of chain, and add there */
+               for (i = module_list; i->next; i = i->next)
+                       ;
+               i->next = m;
+       }
        
-       module_list = m;
-       ast_pthread_mutex_unlock(&modlock);
+       modlistver = rand();
+       ast_mutex_unlock(&modlock);
        if ((res = m->load_module())) {
                ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", m->resource, res);
                ast_unload_resource(resource_name, 0);
@@ -302,12 +391,29 @@ int ast_load_resource(char *resource_name)
        }
        ast_update_use_count();
        return 0;
+}
+
+int ast_load_resource(const char *resource_name)
+{
+       int o;
+       struct ast_config *cfg = NULL;
+       int res;
+
+       /* Keep the module file parsing silent */
+       o = option_verbose;
+       option_verbose = 0;
+       cfg = ast_config_load(AST_MODULE_CONFIG);
+       option_verbose = o;
+       res = __load_resource(resource_name, cfg);
+       if (cfg)
+               ast_config_destroy(cfg);
+       return res;
 }      
 
 static int ast_resource_exists(char *resource)
 {
        struct module *m;
-       if (ast_pthread_mutex_lock(&modlock))
+       if (ast_mutex_lock(&modlock))
                ast_log(LOG_WARNING, "Failed to lock\n");
        m = module_list;
        while(m) {
@@ -315,13 +421,21 @@ static int ast_resource_exists(char *resource)
                        break;
                m = m->next;
        }
-       ast_pthread_mutex_unlock(&modlock);
+       ast_mutex_unlock(&modlock);
        if (m)
                return -1;
        else
                return 0;
 }
 
+static const char *loadorder[] =
+{
+       "res_",
+       "chan_",
+       "pbx_",
+       NULL,
+};
+
 int load_modules()
 {
        struct ast_config *cfg;
@@ -329,7 +443,7 @@ int load_modules()
        char tmp[80];
        if (option_verbose) 
                ast_verbose( "Asterisk Dynamic Loader Starting:\n");
-       cfg = ast_load(AST_MODULE_CONFIG);
+       cfg = ast_config_load(AST_MODULE_CONFIG);
        if (cfg) {
                /* Load explicitly defined modules */
                v = ast_variable_browse(cfg, "modules");
@@ -341,10 +455,10 @@ int load_modules()
                                        ast_verbose( VERBOSE_PREFIX_1 "[%s]", term_color(tmp, v->value, COLOR_BRWHITE, 0, sizeof(tmp)));
                                        fflush(stdout);
                                }
-                               if (ast_load_resource(v->value)) {
+                               if (__load_resource(v->value, cfg)) {
                                        ast_log(LOG_WARNING, "Loading module %s failed!\n", v->value);
                                        if (cfg)
-                                               ast_destroy(cfg);
+                                               ast_config_destroy(cfg);
                                        return -1;
                                }
                        }
@@ -356,13 +470,14 @@ int load_modules()
                DIR *mods;
                struct dirent *d;
                int x;
-               /* Make two passes.  First, load any resource modules, then load the others. */
-               for (x=0;x<2;x++) {
+               /* Loop through each order */
+               for (x=0;x<sizeof(loadorder) / sizeof(loadorder[0]);x++) {
                        mods = opendir((char *)ast_config_AST_MODULE_DIR);
                        if (mods) {
                                while((d = readdir(mods))) {
                                        /* Must end in .so to load it.  */
-                                       if ((strlen(d->d_name) > 3) && (x || !strncasecmp(d->d_name, "res_", 4)) && 
+                                       if ((strlen(d->d_name) > 3) && 
+                                           (!loadorder[x] || !strncasecmp(d->d_name, loadorder[x], strlen(loadorder[x]))) && 
                                            !strcasecmp(d->d_name + strlen(d->d_name) - 3, ".so") &&
                                                !ast_resource_exists(d->d_name)) {
                                                /* It's a shared library -- Just be sure we're allowed to load it -- kinda
@@ -390,10 +505,10 @@ int load_modules()
                                                        ast_verbose( VERBOSE_PREFIX_1 "[%s]", term_color(tmp, d->d_name, COLOR_BRWHITE, 0, sizeof(tmp)));
                                                        fflush(stdout);
                                                }
-                                               if (ast_load_resource(d->d_name)) {
+                                               if (__load_resource(d->d_name, cfg)) {
                                                        ast_log(LOG_WARNING, "Loading module %s failed!\n", d->d_name);
                                                        if (cfg)
-                                                               ast_destroy(cfg);
+                                                               ast_config_destroy(cfg);
                                                        return -1;
                                                }
                                        }
@@ -405,7 +520,7 @@ int load_modules()
                        }
                }
        } 
-       ast_destroy(cfg);
+       ast_config_destroy(cfg);
        return 0;
 }
 
@@ -414,31 +529,32 @@ void ast_update_use_count(void)
        /* Notify any module monitors that the use count for a 
           resource has changed */
        struct loadupdate *m;
-       if (ast_pthread_mutex_lock(&modlock))
+       if (ast_mutex_lock(&modlock))
                ast_log(LOG_WARNING, "Failed to lock\n");
        m = updaters;
        while(m) {
                m->updater();
                m = m->next;
        }
-       ast_pthread_mutex_unlock(&modlock);
+       ast_mutex_unlock(&modlock);
        
 }
 
-int ast_update_module_list(int (*modentry)(char *module, char *description, int usecnt))
+int ast_update_module_list(int (*modentry)(char *module, char *description, int usecnt, char *like), char *like)
 {
        struct module *m;
        int unlock = -1;
-       if (pthread_mutex_trylock(&modlock))
+       int total_mod_loaded = 0;
+       if (ast_mutex_trylock(&modlock))
                unlock = 0;
        m = module_list;
        while(m) {
-               modentry(m->resource, m->description(), m->usecount());
+               total_mod_loaded += modentry(m->resource, m->description(), m->usecount(), like);
                m = m->next;
        }
        if (unlock)
-               ast_pthread_mutex_unlock(&modlock);
-       return 0;
+               ast_mutex_unlock(&modlock);
+       return total_mod_loaded;
 }
 
 int ast_loader_register(int (*v)(void)) 
@@ -447,11 +563,11 @@ int ast_loader_register(int (*v)(void))
        /* XXX Should be more flexible here, taking > 1 verboser XXX */
        if ((tmp = malloc(sizeof (struct loadupdate)))) {
                tmp->updater = v;
-               if (ast_pthread_mutex_lock(&modlock))
+               if (ast_mutex_lock(&modlock))
                        ast_log(LOG_WARNING, "Failed to lock\n");
                tmp->next = updaters;
                updaters = tmp;
-               ast_pthread_mutex_unlock(&modlock);
+               ast_mutex_unlock(&modlock);
                return 0;
        }
        return -1;
@@ -461,7 +577,7 @@ int ast_loader_unregister(int (*v)(void))
 {
        int res = -1;
        struct loadupdate *tmp, *tmpl=NULL;
-       if (ast_pthread_mutex_lock(&modlock))
+       if (ast_mutex_lock(&modlock))
                ast_log(LOG_WARNING, "Failed to lock\n");
        tmp = updaters;
        while(tmp) {
@@ -477,6 +593,6 @@ int ast_loader_unregister(int (*v)(void))
        }
        if (tmp)
                res = 0;
-       ast_pthread_mutex_unlock(&modlock);
+       ast_mutex_unlock(&modlock);
        return res;
 }