kmodloader: fix GCC fanalyzer warnings
[project/ubox.git] / kmodloader.c
index 6f06ee3939f2f3e151357d33cfd165603cb40e4d..b2e7a8b44089a788e51c281ee5fa8ea05b317f51 100644 (file)
 #include <libgen.h>
 #include <glob.h>
 #include <elf.h>
+#include <ctype.h>
 
 #include <libubox/avl.h>
 #include <libubox/avl-cmp.h>
 #include <libubox/utils.h>
 #include <libubox/ulog.h>
+#include <libubox/kvlist.h>
 
 #define DEF_MOD_PATH "/modules/%s/"
 /* duplicated from in-kernel include/linux/module.h */
@@ -44,6 +46,7 @@ enum {
        SCANNED,
        PROBE,
        LOADED,
+       BLACKLISTED,
 };
 
 struct module {
@@ -65,6 +68,7 @@ struct module_node {
 };
 
 static struct avl_tree modules;
+static KVLIST(options, kvlist_strlen);
 
 static char **module_folders = NULL;
 
@@ -332,6 +336,11 @@ static int scan_loaded_modules(void)
                        /* possibly a module outside /lib/modules/<ver>/ */
                        n = alloc_module(m.name, NULL, 0, m.depends, m.size);
                }
+               if (!n) {
+                       ULOG_ERR("Failed to allocate memory for module\n");
+                       return -1;
+               }
+
                n->usage = m.usage;
                n->state = LOADED;
        }
@@ -347,6 +356,7 @@ static struct module* get_module_info(const char *module, const char *name)
        unsigned int offset, size;
        char *map = MAP_FAILED, *strings, *dep = NULL;
        const char **aliases = NULL;
+       const char **aliasesr;
        int naliases = 0;
        struct module *m = NULL;
        struct stat s;
@@ -389,12 +399,13 @@ static struct module* get_module_info(const char *module, const char *name)
                if (!strncmp(strings, "depends=", len + 1))
                        dep = sep;
                else if (!strncmp(strings, "alias=", len + 1)) {
-                       aliases = realloc(aliases, sizeof(sep) * (naliases + 1));
-                       if (!aliases) {
+                       aliasesr = realloc(aliases, sizeof(sep) * (naliases + 1));
+                       if (!aliasesr) {
                                ULOG_ERR("out of memory\n");
                                goto out;
                        }
 
+                       aliases = aliasesr;
                        aliases[naliases++] = sep;
                }
                strings = &sep[strlen(sep)];
@@ -435,15 +446,29 @@ static int scan_module_folder(const char *dir)
        for (j = 0; j < gl.gl_pathc; j++) {
                char *name = get_module_name(gl.gl_pathv[j]);
                struct module *m;
+               char *opts;
 
                if (!name)
                        continue;
 
                m = find_module(name);
+               if (m)
+                       continue;
+
+               m = get_module_info(gl.gl_pathv[j], name);
                if (!m) {
-                       if (!get_module_info(gl.gl_pathv[j], name))
-                               rv |= -1;
+                       rv |= -1;
+                       continue;
                }
+
+               opts = kvlist_get(&options, name);
+               if (!opts)
+                       continue;
+
+               if (*opts == '\x01')
+                       m->state = BLACKLISTED;
+               else
+                       m->opts = strdup(opts);
        }
 
        globfree(&gl);
@@ -563,6 +588,11 @@ static int insert_module(char *path, const char *options)
        struct stat s;
        int fd, ret = -1;
 
+       if (!path) {
+               ULOG_ERR("Path not specified\n");
+               return ret;
+       }
+
        if (stat(path, &s)) {
                ULOG_ERR("missing module %s\n", path);
                return ret;
@@ -901,7 +931,10 @@ static int main_modprobe(int argc, char **argv)
                name = get_module_name(argv[optind]);
                m = find_module(name);
 
-               if (m && m->state == LOADED) {
+               if (m && m->state == BLACKLISTED) {
+                       if (!quiet)
+                               ULOG_INFO("%s is blacklisted\n", name);
+               } else if (m && m->state == LOADED) {
                        if (!quiet)
                                ULOG_INFO("%s is already loaded\n", name);
                } else if (!m) {
@@ -944,7 +977,7 @@ static int main_loader(int argc, char **argv)
        struct module *m;
        glob_t gl;
        char *path;
-       int fail, j;
+       int ret = 0, fail, j;
 
        if (argc > 1)
                dir = argv[1];
@@ -959,13 +992,13 @@ static int main_loader(int argc, char **argv)
        strcat(path, "*");
 
        if (scan_module_folders()) {
-               free (path);
-               return -1;
+               ret = -1;
+               goto free_path;
        }
 
        if (scan_loaded_modules()) {
-               free (path);
-               return -1;
+               ret = -1;
+               goto free_path;
        }
 
        ULOG_INFO("loading kernel modules from %s\n", path);
@@ -996,11 +1029,26 @@ static int main_loader(int argc, char **argv)
                                *opts++ = '\0';
 
                        m = find_module(get_module_name(mod));
-                       if (!m || (m->state == LOADED))
+                       if (!m || m->state == LOADED || m->state == BLACKLISTED)
                                continue;
 
-                       if (opts)
-                               m->opts = strdup(opts);
+                       if (opts) {
+                               if (m->opts) {
+                                       char *prev = m->opts;
+
+                                       fail = asprintf(&m->opts, "%s %s", prev, opts);
+                                       free(prev);
+                                       if (fail < 0) {
+                                               ULOG_ERR("out of memory for opts %s\n", opts);
+                                               free(mod);
+                                               fclose(fp);
+                                               ret = -1;
+                                               goto out;
+                                       }
+                               } else {
+                                       m->opts = strdup(opts);
+                               }
+                       }
                        m->state = PROBE;
                        if (basename(gl.gl_pathv[j])[0] - '0' <= 9)
                                load_modprobe(false);
@@ -1029,9 +1077,10 @@ static int main_loader(int argc, char **argv)
 
 out:
        globfree(&gl);
+free_path:
        free(path);
 
-       return 0;
+       return ret;
 }
 
 static inline char weight(char c)
@@ -1053,6 +1102,82 @@ static int avl_modcmp(const void *k1, const void *k2, void *ptr)
        return (unsigned char)weight(*s1) - (unsigned char)weight(*s2);
 }
 
+static void
+load_options(void)
+{
+       static char buf[512];
+       char *s;
+       FILE *f;
+
+       f = fopen("/etc/modules.conf", "r");
+       if (!f)
+               return;
+
+       while ((s = fgets(buf, sizeof(buf), f)) != NULL) {
+               char *c, *cmd, *mod;
+
+               while (isspace(*s))
+                       s++;
+
+               c = strchr(s, '#');
+               if (c)
+                       *c = 0;
+
+               while (isspace(*s))
+                       s++;
+
+               c = s + strlen(s);
+               while (c > s && isspace(c[-1])) {
+                       c[-1] = 0;
+                       c--;
+               }
+
+               cmd = strsep(&s, " \t");
+               if (!cmd || !*cmd)
+                       continue;
+
+               while (isspace(*s))
+                       s++;
+
+               mod = strsep(&s, " \t");
+               if (!mod || !*mod)
+                       continue;
+
+               if (!strcmp(cmd, "blacklist")) {
+                       kvlist_set(&options, mod, "\x01");
+                       continue;
+               }
+
+               if (!strcmp(cmd, "options")) {
+                       char *prev = kvlist_get(&options, mod);
+                       char *val = NULL;
+
+                       while (isspace(*s))
+                               s++;
+
+                       if (!*s)
+                               continue;
+
+                       if (prev && prev[0] == '\x01')
+                               continue;
+
+                       if (!prev) {
+                               kvlist_set(&options, mod, s);
+                               continue;
+                       }
+
+                       if (asprintf(&val, "%s %s", prev, s) < 0)
+                               continue;
+
+                       kvlist_set(&options, mod, val);
+                       free(val);
+                       continue;
+               }
+       }
+
+       fclose(f);
+}
+
 int main(int argc, char **argv)
 {
        char *exec = basename(*argv);
@@ -1070,6 +1195,8 @@ int main(int argc, char **argv)
        if (!strcmp(exec, "modinfo"))
                return main_modinfo(argc, argv);
 
+       load_options();
+
        if (!strcmp(exec, "modprobe"))
                return main_modprobe(argc, argv);