]> git.ipfire.org Git - thirdparty/kmod.git/blobdiff - libkmod/libkmod-module.c
libkmod: Fix use of sizeof instead of ARRAY_SIZE
[thirdparty/kmod.git] / libkmod / libkmod-module.c
index 3d3325f2b1ee6e047fc78836f2e0b8441900cf03..efdd679851ce1134e4f7df50cc8073afa33168e2 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
 #include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fnmatch.h>
 #include <inttypes.h>
 #include <limits.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 #include <sys/mman.h>
+#include <sys/stat.h>
 #include <sys/syscall.h>
+#include <sys/types.h>
 #include <sys/wait.h>
-#include <fnmatch.h>
-
 #ifdef HAVE_LINUX_MODULE_H
 #include <linux/module.h>
 #endif
 
+#include <shared/util.h>
+
 #include "libkmod.h"
 #include "libkmod-internal.h"
 
  * @short_description: operate on kernel modules
  */
 
+enum kmod_module_builtin {
+    KMOD_MODULE_BUILTIN_UNKNOWN,
+    KMOD_MODULE_BUILTIN_NO,
+    KMOD_MODULE_BUILTIN_YES,
+};
+
 /**
  * kmod_module:
  *
@@ -74,6 +80,13 @@ struct kmod_module {
                bool remove_commands : 1;
        } init;
 
+       /*
+        * mark if module is builtin, i.e. it's present on modules.builtin
+        * file. This is set as soon as it is needed or as soon as we know
+        * about it, i.e. the module was created from builtin lookup.
+        */
+       enum kmod_module_builtin builtin;
+
        /*
         * private field used by kmod_module_get_probe_list() to detect
         * dependency loops
@@ -87,11 +100,11 @@ struct kmod_module {
        bool ignorecmd : 1;
 
        /*
-        * if module was created by searching the modules.builtin file, this
-        * is set. There's nothing much useful one can do with such a
-        * "module", except knowing it's builtin.
+        * set by kmod_module_get_probe_list: indicates whether this is the
+        * module the user asked for or its dependency, or whether this
+        * is a softdep only
         */
-       bool builtin : 1;
+       bool required : 1;
 };
 
 static inline const char *path_join(const char *path, size_t prefixlen,
@@ -163,7 +176,7 @@ int kmod_module_parse_depline(struct kmod_module *mod, char *line)
        p++;
        for (p = strtok_r(p, " \t", &saveptr); p != NULL;
                                        p = strtok_r(NULL, " \t", &saveptr)) {
-               struct kmod_module *depmod;
+               struct kmod_module *depmod = NULL;
                const char *path;
 
                path = path_join(p, dirnamelen, buf);
@@ -205,9 +218,24 @@ void kmod_module_set_visited(struct kmod_module *mod, bool visited)
 
 void kmod_module_set_builtin(struct kmod_module *mod, bool builtin)
 {
-       mod->builtin = builtin;
+       mod->builtin =
+               builtin ? KMOD_MODULE_BUILTIN_YES : KMOD_MODULE_BUILTIN_NO;
 }
 
+void kmod_module_set_required(struct kmod_module *mod, bool required)
+{
+       mod->required = required;
+}
+
+bool kmod_module_is_builtin(struct kmod_module *mod)
+{
+       if (mod->builtin == KMOD_MODULE_BUILTIN_UNKNOWN) {
+               kmod_module_set_builtin(mod,
+                                       kmod_lookup_alias_is_builtin(mod->ctx, mod->name));
+       }
+
+       return mod->builtin == KMOD_MODULE_BUILTIN_YES;
+}
 /*
  * Memory layout with alias:
  *
@@ -403,15 +431,18 @@ KMOD_EXPORT int kmod_module_new_from_path(struct kmod_ctx *ctx,
                        return -EEXIST;
                }
 
-               *mod = kmod_module_ref(m);
-               return 0;
-       }
+               kmod_module_ref(m);
+       } else {
+               err = kmod_module_new(ctx, name, name, namelen, NULL, 0, &m);
+               if (err < 0) {
+                       free(abspath);
+                       return err;
+               }
 
-       err = kmod_module_new(ctx, name, name, namelen, NULL, 0, &m);
-       if (err < 0)
-               return err;
+               m->path = abspath;
+       }
 
-       m->path = abspath;
+       m->builtin = KMOD_MODULE_BUILTIN_NO;
        *mod = m;
 
        return 0;
@@ -468,13 +499,26 @@ KMOD_EXPORT struct kmod_module *kmod_module_ref(struct kmod_module *mod)
        return mod;
 }
 
-#define CHECK_ERR_AND_FINISH(_err, _label_err, _list, label_finish)    \
-       do {                                                            \
-               if ((_err) < 0)                                         \
-                       goto _label_err;                                \
-               if (*(_list) != NULL)                                   \
-                       goto finish;                                    \
-       } while (0)
+typedef int (*lookup_func)(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+
+static int __kmod_module_new_from_lookup(struct kmod_ctx *ctx, const lookup_func lookup[],
+                                        size_t lookup_count, const char *s,
+                                        struct kmod_list **list)
+{
+       unsigned int i;
+
+       for (i = 0; i < lookup_count; i++) {
+               int err;
+
+               err = lookup[i](ctx, s, list);
+               if (err < 0 && err != -ENOSYS)
+                       return err;
+               else if (*list != NULL)
+                       return 0;
+       }
+
+       return 0;
+}
 
 /**
  * kmod_module_new_from_lookup:
@@ -490,7 +534,7 @@ KMOD_EXPORT struct kmod_module *kmod_module_ref(struct kmod_module *mod)
  *
  * The search order is: 1. aliases in configuration file; 2. module names in
  * modules.dep index; 3. symbol aliases in modules.symbols index; 4. aliases
- * in modules.alias index.
+ * from install commands; 5. builtin indexes from kernel.
  *
  * The initial refcount is 1, and needs to be decremented to release the
  * resources of the kmod_module. The returned @list must be released by
@@ -507,8 +551,17 @@ KMOD_EXPORT int kmod_module_new_from_lookup(struct kmod_ctx *ctx,
                                                const char *given_alias,
                                                struct kmod_list **list)
 {
-       int err;
+       const lookup_func lookup[] = {
+               kmod_lookup_alias_from_config,
+               kmod_lookup_alias_from_moddep_file,
+               kmod_lookup_alias_from_symbols_file,
+               kmod_lookup_alias_from_commands,
+               kmod_lookup_alias_from_aliases_file,
+               kmod_lookup_alias_from_builtin_file,
+               kmod_lookup_alias_from_kernel_builtin_file,
+       };
        char alias[PATH_MAX];
+       int err;
 
        if (ctx == NULL || given_alias == NULL)
                return -ENOENT;
@@ -525,40 +578,75 @@ KMOD_EXPORT int kmod_module_new_from_lookup(struct kmod_ctx *ctx,
 
        DBG(ctx, "input alias=%s, normalized=%s\n", given_alias, alias);
 
-       /* Aliases from config file override all the others */
-       err = kmod_lookup_alias_from_config(ctx, alias, list);
-       CHECK_ERR_AND_FINISH(err, fail, list, finish);
+       err = __kmod_module_new_from_lookup(ctx, lookup, ARRAY_SIZE(lookup),
+                                           alias, list);
+
+       DBG(ctx, "lookup=%s found=%d\n", alias, err >= 0 && *list);
+
+       if (err < 0) {
+               kmod_module_unref_list(*list);
+               *list = NULL;
+       }
+
+       return err;
+}
+
+/**
+ * kmod_module_new_from_name_lookup:
+ * @ctx: kmod library context
+ * @modname: module name to look for
+ * @mod: returned module on success
+ *
+ * Lookup by module name, without considering possible aliases. This is similar
+ * to kmod_module_new_from_lookup(), but don't consider as source indexes and
+ * configurations that work with aliases. When succesful, this always resolves
+ * to one and only one module.
+ *
+ * The search order is: 1. module names in modules.dep index;
+ * 2. builtin indexes from kernel.
+ *
+ * The initial refcount is 1, and needs to be decremented to release the
+ * resources of the kmod_module. Since libkmod keeps track of all
+ * kmod_modules created, they are all released upon @ctx destruction too. Do
+ * not unref @ctx before all the desired operations with the returned list are
+ * completed.
+ *
+ * Returns: 0 on success or < 0 otherwise. It fails if any of the lookup
+ * methods failed, which is basically due to memory allocation failure. If
+ * module is not found, it still returns 0, but @mod is left untouched.
+ */
+KMOD_EXPORT int kmod_module_new_from_name_lookup(struct kmod_ctx *ctx,
+                                                const char *modname,
+                                                struct kmod_module **mod)
+{
+       const lookup_func lookup[] = {
+               kmod_lookup_alias_from_moddep_file,
+               kmod_lookup_alias_from_builtin_file,
+               kmod_lookup_alias_from_kernel_builtin_file,
+       };
+       char name_norm[PATH_MAX];
+       struct kmod_list *list = NULL;
+       int err;
+
+       if (ctx == NULL || modname == NULL || mod == NULL)
+               return -ENOENT;
 
-       DBG(ctx, "lookup modules.dep %s\n", alias);
-       err = kmod_lookup_alias_from_moddep_file(ctx, alias, list);
-       CHECK_ERR_AND_FINISH(err, fail, list, finish);
+       modname_normalize(modname, name_norm, NULL);
 
-       DBG(ctx, "lookup modules.symbols %s\n", alias);
-       err = kmod_lookup_alias_from_symbols_file(ctx, alias, list);
-       CHECK_ERR_AND_FINISH(err, fail, list, finish);
+       DBG(ctx, "input modname=%s, normalized=%s\n", modname, name_norm);
 
-       DBG(ctx, "lookup install and remove commands %s\n", alias);
-       err = kmod_lookup_alias_from_commands(ctx, alias, list);
-       CHECK_ERR_AND_FINISH(err, fail, list, finish);
+       err = __kmod_module_new_from_lookup(ctx, lookup, ARRAY_SIZE(lookup),
+                                           name_norm, &list);
 
-       DBG(ctx, "lookup modules.aliases %s\n", alias);
-       err = kmod_lookup_alias_from_aliases_file(ctx, alias, list);
-       CHECK_ERR_AND_FINISH(err, fail, list, finish);
+       DBG(ctx, "lookup=%s found=%d\n", name_norm, err >= 0 && list);
 
-       DBG(ctx, "lookup modules.builtin %s\n", alias);
-       err = kmod_lookup_alias_from_builtin_file(ctx, alias, list);
-       CHECK_ERR_AND_FINISH(err, fail, list, finish);
+       if (err >= 0 && list != NULL)
+               *mod = kmod_module_get_module(list);
+
+       kmod_module_unref_list(list);
 
-finish:
-       DBG(ctx, "lookup %s=%d, list=%p\n", alias, err, *list);
-       return err;
-fail:
-       DBG(ctx, "Failed to lookup %s\n", alias);
-       kmod_module_unref_list(*list);
-       *list = NULL;
        return err;
 }
-#undef CHECK_ERR_AND_FINISH
 
 /**
  * kmod_module_unref_list:
@@ -790,8 +878,7 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
        int err;
        const void *mem;
        off_t size;
-       struct kmod_file *file;
-       struct kmod_elf *elf = NULL;
+       struct kmod_elf *elf;
        const char *path;
        const char *args = options ? options : "";
 
@@ -801,16 +888,18 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
        path = kmod_module_get_path(mod);
        if (path == NULL) {
                ERR(mod->ctx, "could not find module by name='%s'\n", mod->name);
-               return -ENOSYS;
+               return -ENOENT;
        }
 
-       file = kmod_file_open(mod->ctx, path);
-       if (file == NULL) {
-               err = -errno;
-               return err;
+       if (!mod->file) {
+               mod->file = kmod_file_open(mod->ctx, path);
+               if (mod->file == NULL) {
+                       err = -errno;
+                       return err;
+               }
        }
 
-       if (kmod_file_get_direct(file)) {
+       if (kmod_file_get_direct(mod->file)) {
                unsigned int kernel_flags = 0;
 
                if (flags & KMOD_INSERT_FORCE_VERMAGIC)
@@ -818,19 +907,16 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
                if (flags & KMOD_INSERT_FORCE_MODVERSION)
                        kernel_flags |= MODULE_INIT_IGNORE_MODVERSIONS;
 
-               err = finit_module(kmod_file_get_fd(file), args, kernel_flags);
+               err = finit_module(kmod_file_get_fd(mod->file), args, kernel_flags);
                if (err == 0 || errno != ENOSYS)
                        goto init_finished;
        }
 
-       size = kmod_file_get_size(file);
-       mem = kmod_file_get_contents(file);
-
        if (flags & (KMOD_INSERT_FORCE_VERMAGIC | KMOD_INSERT_FORCE_MODVERSION)) {
-               elf = kmod_elf_new(mem, size);
+               elf = kmod_file_get_elf(mod->file);
                if (elf == NULL) {
                        err = -errno;
-                       goto elf_failed;
+                       return err;
                }
 
                if (flags & KMOD_INSERT_FORCE_MODVERSION) {
@@ -846,7 +932,10 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
                }
 
                mem = kmod_elf_get_memory(elf);
+       } else {
+               mem = kmod_file_get_contents(mod->file);
        }
+       size = kmod_file_get_size(mod->file);
 
        err = init_module(mem, size, args);
 init_finished:
@@ -854,12 +943,6 @@ init_finished:
                err = -errno;
                INFO(mod->ctx, "Failed to insert module '%s': %m\n", path);
        }
-
-       if (elf != NULL)
-               kmod_elf_unref(elf);
-elf_failed:
-       kmod_file_unref(file);
-
        return err;
 }
 
@@ -917,7 +1000,8 @@ KMOD_EXPORT int kmod_module_apply_filter(const struct kmod_ctx *ctx,
                                module_is_blacklisted(mod))
                        continue;
 
-               if ((filter_type & KMOD_FILTER_BUILTIN) && mod->builtin)
+               if ((filter_type & KMOD_FILTER_BUILTIN)
+                   && kmod_module_is_builtin(mod))
                        continue;
 
                node = kmod_list_append(*output, mod);
@@ -948,14 +1032,19 @@ static int command_do(struct kmod_module *mod, const char *type,
        err = system(cmd);
        unsetenv("MODPROBE_MODULE");
 
-       if (err == -1 || WEXITSTATUS(err)) {
-               ERR(mod->ctx, "Error running %s command for %s\n",
-                                                               type, modname);
-               if (err != -1)
-                       err = -WEXITSTATUS(err);
+       if (err == -1) {
+               ERR(mod->ctx, "Could not run %s command '%s' for module %s: %m\n",
+                   type, cmd, modname);
+               return -EINVAL;
        }
 
-       return err;
+       if (WEXITSTATUS(err)) {
+               ERR(mod->ctx, "Error running %s command '%s' for module %s: retcode %d\n",
+                   type, cmd, modname, WEXITSTATUS(err));
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
 struct probe_insert_cb {
@@ -1040,6 +1129,7 @@ static char *module_options_concat(const char *opt, const char *xopt)
 }
 
 static int __kmod_module_get_probe_list(struct kmod_module *mod,
+                                               bool required,
                                                bool ignorecmd,
                                                struct kmod_list **list);
 
@@ -1059,7 +1149,7 @@ static int __kmod_module_fill_softdep(struct kmod_module *mod,
 
        kmod_list_foreach(l, pre) {
                struct kmod_module *m = l->data;
-               err = __kmod_module_get_probe_list(m, false, list);
+               err = __kmod_module_get_probe_list(m, false, false, list);
                if (err < 0)
                        goto fail;
        }
@@ -1075,7 +1165,7 @@ static int __kmod_module_fill_softdep(struct kmod_module *mod,
 
        kmod_list_foreach(l, post) {
                struct kmod_module *m = l->data;
-               err = __kmod_module_get_probe_list(m, false, list);
+               err = __kmod_module_get_probe_list(m, false, false, list);
                if (err < 0)
                        goto fail;
        }
@@ -1089,6 +1179,7 @@ fail:
 
 /* re-entrant */
 static int __kmod_module_get_probe_list(struct kmod_module *mod,
+                                               bool required,
                                                bool ignorecmd,
                                                struct kmod_list **list)
 {
@@ -1103,6 +1194,19 @@ static int __kmod_module_get_probe_list(struct kmod_module *mod,
        mod->visited = true;
 
        dep = kmod_module_get_dependencies(mod);
+       if (required) {
+               /*
+                * Called from kmod_module_probe_insert_module(); set the
+                * ->required flag on mod and all its dependencies before
+                * they are possibly visited through some softdeps.
+                */
+               mod->required = true;
+               kmod_list_foreach(l, dep) {
+                       struct kmod_module *m = l->data;
+                       m->required = true;
+               }
+       }
+
        kmod_list_foreach(l, dep) {
                struct kmod_module *m = l->data;
                err = __kmod_module_fill_softdep(m, list);
@@ -1140,8 +1244,9 @@ static int kmod_module_get_probe_list(struct kmod_module *mod,
         * Make sure we don't get screwed by previous calls to this function
         */
        kmod_set_modules_visited(mod->ctx, false);
+       kmod_set_modules_required(mod->ctx, false);
 
-       err = __kmod_module_get_probe_list(mod, ignorecmd, list);
+       err = __kmod_module_get_probe_list(mod, true, ignorecmd, list);
        if (err < 0) {
                kmod_module_unref_list(*list);
                *list = NULL;
@@ -1302,8 +1407,12 @@ finish_module:
                                (flags & KMOD_PROBE_FAIL_ON_LOADED))
                        break;
 
-               if (err == -EEXIST)
+               /*
+                * Ignore errors from softdeps
+                */
+               if (err == -EEXIST || !m->required)
                        err = 0;
+
                else if (err < 0)
                        break;
        }
@@ -1616,13 +1725,14 @@ KMOD_EXPORT int kmod_module_new_from_loaded(struct kmod_ctx *ctx,
                struct kmod_module *m;
                struct kmod_list *node;
                int err;
+               size_t len = strlen(line);
                char *saveptr, *name = strtok_r(line, " \t", &saveptr);
 
                err = kmod_module_new_from_name(ctx, name, &m);
                if (err < 0) {
                        ERR(ctx, "could not get module from name '%s': %s\n",
                                name, strerror(-err));
-                       continue;
+                       goto eat_line;
                }
 
                node = kmod_list_append(l, m);
@@ -1632,6 +1742,9 @@ KMOD_EXPORT int kmod_module_new_from_loaded(struct kmod_ctx *ctx,
                        ERR(ctx, "out of memory\n");
                        kmod_module_unref(m);
                }
+eat_line:
+               while (line[len - 1] != '\n' && fgets(line, sizeof(line), fp))
+                       len = strlen(line);
        }
 
        fclose(fp);
@@ -1686,7 +1799,8 @@ KMOD_EXPORT int kmod_module_get_initstate(const struct kmod_module *mod)
        if (mod == NULL)
                return -ENOENT;
 
-       if (mod->builtin)
+       /* remove const: this can only change internal state */
+       if (kmod_module_is_builtin((struct kmod_module *)mod))
                return KMOD_MODULE_BUILTIN;
 
        pathlen = snprintf(path, sizeof(path),
@@ -1702,7 +1816,7 @@ KMOD_EXPORT int kmod_module_get_initstate(const struct kmod_module *mod)
                        struct stat st;
                        path[pathlen - (sizeof("/initstate") - 1)] = '\0';
                        if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
-                               return KMOD_MODULE_BUILTIN;
+                               return KMOD_MODULE_COMING;
                }
 
                DBG(mod->ctx, "could not open '%s': %s\n",
@@ -1756,7 +1870,7 @@ KMOD_EXPORT long kmod_module_get_size(const struct kmod_module *mod)
         * loaded.
         */
        snprintf(line, sizeof(line), "/sys/module/%s", mod->name);
-       dfd = open(line, O_RDONLY);
+       dfd = open(line, O_RDONLY|O_CLOEXEC);
        if (dfd < 0)
                return -errno;
 
@@ -1775,16 +1889,18 @@ KMOD_EXPORT long kmod_module_get_size(const struct kmod_module *mod)
                int err = -errno;
                ERR(mod->ctx,
                    "could not open /proc/modules: %s\n", strerror(errno));
+               close(dfd);
                return err;
        }
 
        while (fgets(line, sizeof(line), fp)) {
+               size_t len = strlen(line);
                char *saveptr, *endptr, *tok = strtok_r(line, " \t", &saveptr);
                long value;
 
                lineno++;
                if (tok == NULL || !streq(tok, mod->name))
-                       continue;
+                       goto eat_line;
 
                tok = strtok_r(NULL, " \t", &saveptr);
                if (tok == NULL) {
@@ -1802,6 +1918,9 @@ KMOD_EXPORT long kmod_module_get_size(const struct kmod_module *mod)
 
                size = value;
                break;
+eat_line:
+               while (line[len - 1] != '\n' && fgets(line, sizeof(line), fp))
+                       len = strlen(line);
        }
        fclose(fp);
 
@@ -1817,7 +1936,7 @@ done:
  * Get the ref count of this @mod, as returned by Linux Kernel, by reading
  * /sys filesystem.
  *
- * Returns: 0 on success or < 0 on failure.
+ * Returns: the reference count on success or < 0 on failure.
  */
 KMOD_EXPORT int kmod_module_get_refcnt(const struct kmod_module *mod)
 {
@@ -2137,6 +2256,63 @@ static struct kmod_list *kmod_module_info_append(struct kmod_list **list, const
        return n;
 }
 
+static char *kmod_module_hex_to_str(const char *hex, size_t len)
+{
+       char *str;
+       int i;
+       int j;
+       const size_t line_limit = 20;
+       size_t str_len;
+
+       str_len = len * 3; /* XX: or XX\0 */
+       str_len += ((str_len + line_limit - 1) / line_limit - 1) * 3; /* \n\t\t */
+
+       str = malloc(str_len);
+       if (str == NULL)
+               return NULL;
+
+       for (i = 0, j = 0; i < (int)len; i++) {
+               j += sprintf(str + j, "%02X", (unsigned char)hex[i]);
+               if (i < (int)len - 1) {
+                       str[j++] = ':';
+
+                       if ((i + 1) % line_limit == 0)
+                               j += sprintf(str + j, "\n\t\t");
+               }
+       }
+       return str;
+}
+
+static struct kmod_list *kmod_module_info_append_hex(struct kmod_list **list,
+                                                    const char *key,
+                                                    size_t keylen,
+                                                    const char *value,
+                                                    size_t valuelen)
+{
+       char *hex;
+       struct kmod_list *n;
+
+       if (valuelen > 0) {
+               /* Display as 01:12:DE:AD:BE:EF:... */
+               hex = kmod_module_hex_to_str(value, valuelen);
+               if (hex == NULL)
+                       goto list_error;
+               n = kmod_module_info_append(list, key, keylen, hex, strlen(hex));
+               free(hex);
+               if (n == NULL)
+                       goto list_error;
+       } else {
+               n = kmod_module_info_append(list, key, keylen, NULL, 0);
+               if (n == NULL)
+                       goto list_error;
+       }
+
+       return n;
+
+list_error:
+       return NULL;
+}
+
 /**
  * kmod_module_get_info:
  * @mod: kmod module
@@ -2153,27 +2329,36 @@ static struct kmod_list *kmod_module_info_append(struct kmod_list **list, const
  *
  * After use, free the @list by calling kmod_module_info_free_list().
  *
- * Returns: 0 on success or < 0 otherwise.
+ * Returns: number of entries in @list on success or < 0 otherwise.
  */
 KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, struct kmod_list **list)
 {
        struct kmod_elf *elf;
        char **strings;
        int i, count, ret = -ENOMEM;
-       struct kmod_signature_info sig_info;
+       struct kmod_signature_info sig_info = {};
 
        if (mod == NULL || list == NULL)
                return -ENOENT;
 
        assert(*list == NULL);
 
-       elf = kmod_module_get_elf(mod);
-       if (elf == NULL)
-               return -errno;
+       /* remove const: this can only change internal state */
+       if (kmod_module_is_builtin((struct kmod_module *)mod)) {
+               count = kmod_builtin_get_modinfo(mod->ctx,
+                                               kmod_module_get_name(mod),
+                                               &strings);
+               if (count < 0)
+                       return count;
+       } else {
+               elf = kmod_module_get_elf(mod);
+               if (elf == NULL)
+                       return -errno;
 
-       count = kmod_elf_get_strings(elf, ".modinfo", &strings);
-       if (count < 0)
-               return count;
+               count = kmod_elf_get_strings(elf, ".modinfo", &strings);
+               if (count < 0)
+                       return count;
+       }
 
        for (i = 0; i < count; i++) {
                struct kmod_list *n;
@@ -2197,9 +2382,14 @@ KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, struct kmod_
                        goto list_error;
        }
 
-       if (kmod_module_signature_info(mod->file, &sig_info)) {
+       if (mod->file && kmod_module_signature_info(mod->file, &sig_info)) {
                struct kmod_list *n;
-               char *key_hex;
+
+               n = kmod_module_info_append(list, "sig_id", strlen("sig_id"),
+                               sig_info.id_type, strlen(sig_info.id_type));
+               if (n == NULL)
+                       goto list_error;
+               count++;
 
                n = kmod_module_info_append(list, "signer", strlen("signer"),
                                sig_info.signer, sig_info.signer_len);
@@ -2207,19 +2397,10 @@ KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, struct kmod_
                        goto list_error;
                count++;
 
-               /* Display the key id as 01:12:DE:AD:BE:EF:... */
-               key_hex = malloc(sig_info.key_id_len * 3);
-               if (key_hex == NULL)
-                       goto list_error;
-               for (i = 0; i < (int)sig_info.key_id_len; i++) {
-                       sprintf(key_hex + i * 3, "%02X",
-                                       (unsigned char)sig_info.key_id[i]);
-                       if (i < (int)sig_info.key_id_len - 1)
-                               key_hex[i * 3 + 2] = ':';
-               }
-               n = kmod_module_info_append(list, "sig_key", strlen("sig_key"),
-                               key_hex, sig_info.key_id_len * 3 - 1);
-               free(key_hex);
+
+               n = kmod_module_info_append_hex(list, "sig_key", strlen("sig_key"),
+                                               sig_info.key_id,
+                                               sig_info.key_id_len);
                if (n == NULL)
                        goto list_error;
                count++;
@@ -2232,13 +2413,25 @@ KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, struct kmod_
                count++;
 
                /*
-                * Omit sig_info.id_type and sig_info.algo for now, as these
+                * Omit sig_info.algo for now, as these
                 * are currently constant.
                 */
+               n = kmod_module_info_append_hex(list, "signature",
+                                               strlen("signature"),
+                                               sig_info.sig,
+                                               sig_info.sig_len);
+
+               if (n == NULL)
+                       goto list_error;
+               count++;
+
        }
        ret = count;
 
 list_error:
+       /* aux structures freed in normal case also */
+       kmod_module_signature_info_free(&sig_info);
+
        if (ret < 0) {
                kmod_module_info_free_list(*list);
                *list = NULL;
@@ -2401,7 +2594,7 @@ KMOD_EXPORT const char *kmod_module_version_get_symbol(const struct kmod_list *e
 {
        struct kmod_module_version *version;
 
-       if (entry == NULL)
+       if (entry == NULL || entry->data == NULL)
                return NULL;
 
        version = entry->data;
@@ -2414,14 +2607,13 @@ KMOD_EXPORT const char *kmod_module_version_get_symbol(const struct kmod_list *e
  *
  * Get the crc of a kmod module version.
  *
- * Returns: the crc of this kmod module version on success or NULL on
- * failure. The string is owned by the version, do not free it.
+ * Returns: the crc of this kmod module version if available, otherwise default to 0.
  */
 KMOD_EXPORT uint64_t kmod_module_version_get_crc(const struct kmod_list *entry)
 {
        struct kmod_module_version *version;
 
-       if (entry == NULL)
+       if (entry == NULL || entry->data == NULL)
                return 0;
 
        version = entry->data;
@@ -2542,7 +2734,7 @@ KMOD_EXPORT const char *kmod_module_symbol_get_symbol(const struct kmod_list *en
 {
        struct kmod_module_symbol *symbol;
 
-       if (entry == NULL)
+       if (entry == NULL || entry->data == NULL)
                return NULL;
 
        symbol = entry->data;
@@ -2555,14 +2747,13 @@ KMOD_EXPORT const char *kmod_module_symbol_get_symbol(const struct kmod_list *en
  *
  * Get the crc of a kmod module symbol.
  *
- * Returns: the crc of this kmod module symbol on success or NULL on
- * failure. The string is owned by the symbol, do not free it.
+ * Returns: the crc of this kmod module symbol if available, otherwise default to 0.
  */
 KMOD_EXPORT uint64_t kmod_module_symbol_get_crc(const struct kmod_list *entry)
 {
        struct kmod_module_symbol *symbol;
 
-       if (entry == NULL)
+       if (entry == NULL || entry->data == NULL)
                return 0;
 
        symbol = entry->data;
@@ -2688,7 +2879,7 @@ KMOD_EXPORT const char *kmod_module_dependency_symbol_get_symbol(const struct km
 {
        struct kmod_module_dependency_symbol *dependency_symbol;
 
-       if (entry == NULL)
+       if (entry == NULL || entry->data == NULL)
                return NULL;
 
        dependency_symbol = entry->data;
@@ -2701,14 +2892,13 @@ KMOD_EXPORT const char *kmod_module_dependency_symbol_get_symbol(const struct km
  *
  * Get the crc of a kmod module dependency_symbol.
  *
- * Returns: the crc of this kmod module dependency_symbol on success or NULL on
- * failure. The string is owned by the dependency_symbol, do not free it.
+ * Returns: the crc of this kmod module dependency_symbol if available, otherwise default to 0.
  */
 KMOD_EXPORT uint64_t kmod_module_dependency_symbol_get_crc(const struct kmod_list *entry)
 {
        struct kmod_module_dependency_symbol *dependency_symbol;
 
-       if (entry == NULL)
+       if (entry == NULL || entry->data == NULL)
                return 0;
 
        dependency_symbol = entry->data;
@@ -2728,7 +2918,7 @@ KMOD_EXPORT int kmod_module_dependency_symbol_get_bind(const struct kmod_list *e
 {
        struct kmod_module_dependency_symbol *dependency_symbol;
 
-       if (entry == NULL)
+       if (entry == NULL || entry->data == NULL)
                return 0;
 
        dependency_symbol = entry->data;
@@ -2748,3 +2938,46 @@ KMOD_EXPORT void kmod_module_dependency_symbols_free_list(struct kmod_list *list
                list = kmod_list_remove(list);
        }
 }
+
+/**
+ * kmod_module_get_builtin:
+ * @ctx: kmod library context
+ * @list: where to save the builtin module list
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+int kmod_module_get_builtin(struct kmod_ctx *ctx, struct kmod_list **list)
+{
+       struct kmod_builtin_iter *iter;
+       int err = 0;
+
+       iter = kmod_builtin_iter_new(ctx);
+       if (!iter)
+               return -errno;
+
+       while (kmod_builtin_iter_next(iter)) {
+               struct kmod_module *mod = NULL;
+               char modname[PATH_MAX];
+
+               if (!kmod_builtin_iter_get_modname(iter, modname)) {
+                       err = -errno;
+                       goto fail;
+               }
+
+               err = kmod_module_new_from_name(ctx, modname, &mod);
+               if (err < 0)
+                       goto fail;
+
+               kmod_module_set_builtin(mod, true);
+
+               *list = kmod_list_append(*list, mod);
+       }
+
+       kmod_builtin_iter_free(iter);
+       return err;
+fail:
+       kmod_builtin_iter_free(iter);
+       kmod_module_unref_list(*list);
+       *list = NULL;
+       return err;
+}