Add libkmod-loaded to handle live modules information
All the functions needed by a lsmod binary are in place.
test/test-loaded.c implements it with the same output of lsmod.
diff --git a/Makefile.am b/Makefile.am
index 57a1da3..a6b81db 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -31,7 +31,8 @@
libkmod/libkmod-private.h \
libkmod/libkmod-util.h \
libkmod/libkmod.c \
- libkmod/libkmod-list.c
+ libkmod/libkmod-list.c \
+ libkmod/libkmod-loaded.c
EXTRA_DIST += libkmod/libkmod.sym
@@ -42,8 +43,11 @@
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libkmod/libkmod.pc
-TESTS = test/test-init
+TESTS = test/test-init test/test-loaded
-check_PROGRAMS = test/test-init
+check_PROGRAMS = test/test-init test/test-loaded
test_test_init_SOURCES = test/test-init.c
test_test_init_LDADD = libkmod/libkmod.la
+
+test_test_loaded_SOURCES = test/test-loaded.c
+test_test_loaded_LDADD = libkmod/libkmod.la
diff --git a/libkmod/libkmod-loaded.c b/libkmod/libkmod-loaded.c
new file mode 100644
index 0000000..b0bed74
--- /dev/null
+++ b/libkmod/libkmod-loaded.c
@@ -0,0 +1,228 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011 ProFUSION embedded systems
+ * Copyright (C) 2011 Lucas De Marchi <lucas.de.marchi@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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
+ */
+
+#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 <inttypes.h>
+
+#include "libkmod.h"
+#include "libkmod-private.h"
+
+/**
+ * SECTION:libkmod-loaded
+ * @short_description: currently loaded modules
+ *
+ * Information about currently loaded modules, as reported by Linux kernel
+ */
+
+/**
+ * kmod_loaded:
+ *
+ * Opaque object representing a loaded module.
+ */
+struct kmod_loaded {
+ struct kmod_ctx *ctx;
+ int refcount;
+ struct kmod_list *modules;
+ bool parsed;
+};
+
+struct kmod_loaded_module {
+ char *name;
+ long size;
+ int use_count;
+ char *deps;
+ uintptr_t addr;
+};
+
+KMOD_EXPORT int kmod_loaded_new(struct kmod_ctx *ctx, struct kmod_loaded **mod)
+{
+ struct kmod_loaded *m;
+
+ m = calloc(1, sizeof(*m));
+ if (m == NULL)
+ return -ENOMEM;
+
+ m->refcount = 1;
+ m->ctx = ctx;
+ *mod = m;
+ return 0;
+}
+
+KMOD_EXPORT struct kmod_loaded *kmod_loaded_ref(struct kmod_loaded *mod)
+{
+ if (mod == NULL)
+ return NULL;
+ mod->refcount++;
+ return mod;
+}
+
+static void loaded_modules_free_module(struct kmod_loaded_module *m)
+{
+ free(m->name);
+ free(m->deps);
+ free(m);
+}
+
+static void loaded_modules_free(struct kmod_loaded *mod)
+{
+ while (mod->modules != NULL) {
+ loaded_modules_free_module(mod->modules->data);
+ mod->modules = kmod_list_remove(mod->modules);
+ }
+}
+
+KMOD_EXPORT struct kmod_loaded *kmod_loaded_unref(struct kmod_loaded *mod)
+{
+ if (mod == NULL)
+ return NULL;
+ mod->refcount--;
+ dbg(mod->ctx, "kmod_loaded %p released\n", mod);
+ loaded_modules_free(mod);
+ free(mod);
+ return NULL;
+}
+
+static int loaded_modules_parse(struct kmod_loaded *mod,
+ struct kmod_list **list)
+{
+ struct kmod_list *l = NULL;
+ FILE *fp;
+ char line[4096];
+
+ fp = fopen("/proc/modules", "r");
+ if (fp == NULL)
+ return -errno;
+
+ while (fgets(line, sizeof(line), fp)) {
+ char *tok;
+ struct kmod_loaded_module *m;
+
+ m = calloc(1, sizeof(*m));
+ if (m == NULL)
+ goto err;
+
+ tok = strtok(line, " \t");
+ m->name = strdup(tok);
+
+ tok = strtok(NULL, " \t\n");
+ m->size = atoi(tok);
+
+ /* Null if no module unloading is supported */
+ tok = strtok(NULL, " \t\n");
+ if (tok == NULL)
+ goto done;
+
+ m->use_count = atoi(tok);
+ tok = strtok(NULL, "\n");
+ if (tok == NULL)
+ goto done;
+
+ /* Strip trailing comma */
+ if (strchr(tok, ',')) {
+ char *end;
+ tok = strtok(tok, " \t");
+ end = &tok[strlen(tok) - 1];
+ if (*end == ',')
+ *end = '\0';
+ m->deps = strdup(tok);
+ tok = &end[2];
+ } else if (tok[0] == '-' && tok[1] == '\0')
+ goto done;
+ else if (tok[0] == '-' && isspace(tok[1]))
+ tok = &tok[3];
+
+ tok = strtok(tok, " \t\n");
+ if (tok == NULL)
+ goto done;
+
+ tok = strtok(NULL, " \t\n");
+ if (tok == NULL)
+ goto done;
+
+ sscanf(tok, "%" SCNxPTR, &m->addr);
+
+done:
+ l = kmod_list_append(l, m);
+ }
+
+ fclose(fp);
+ mod->parsed = 1;
+ *list = l;
+
+ return 0;
+
+err:
+ fclose(fp);
+ mod->modules = l;
+ loaded_modules_free(mod);
+ mod->modules = NULL;
+ return -ENOMEM;
+}
+
+KMOD_EXPORT int kmod_loaded_get_list(struct kmod_loaded *mod,
+ struct kmod_list **list)
+{
+ if (mod == NULL)
+ return -ENOENT;
+
+ if (!mod->parsed) {
+ int err = loaded_modules_parse(mod, &mod->modules);
+ if (err < 0)
+ return err;
+ }
+
+ *list = mod->modules;
+
+ return 0;
+}
+
+KMOD_EXPORT int kmod_loaded_get_module_info(struct kmod_list *entry,
+ const char **name,
+ long *size, int *use_count,
+ const char **deps,
+ uintptr_t *addr)
+{
+ struct kmod_loaded_module *m;
+
+ if (entry == NULL)
+ return -ENOENT;
+
+ m = entry->data;
+
+ if (name)
+ *name = m->name;
+ if (size)
+ *size = m->size;
+ if (use_count)
+ *use_count = m->use_count;
+ if (addr)
+ *addr = m->addr;
+ if (deps)
+ *deps = m->deps;
+
+ return 0;
+}
diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h
index 2f3006d..93c5da1 100644
--- a/libkmod/libkmod.h
+++ b/libkmod/libkmod.h
@@ -22,6 +22,7 @@
#define _LIBABC_H_
#include <stdarg.h>
+#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
@@ -64,4 +65,13 @@
} /* extern "C" */
#endif
+struct kmod_loaded;
+int kmod_loaded_new(struct kmod_ctx *ctx, struct kmod_loaded **mod);
+struct kmod_loaded *kmod_loaded_ref(struct kmod_loaded *mod);
+struct kmod_loaded *kmod_loaded_unref(struct kmod_loaded *mod);
+int kmod_loaded_get_list(struct kmod_loaded *mod, struct kmod_list **list);
+int kmod_loaded_get_module_info(struct kmod_list *entry, const char **name,
+ long *size, int *use_count, const char **deps,
+ uintptr_t *addr);
+
#endif
diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym
index 3a06d74..0482ada 100644
--- a/libkmod/libkmod.sym
+++ b/libkmod/libkmod.sym
@@ -10,6 +10,11 @@
kmod_set_userdata;
kmod_unref;
kmod_list_next;
+ kmod_loaded_new;
+ kmod_loaded_ref;
+ kmod_loaded_unref;
+ kmod_loaded_get_list;
+ kmod_loaded_get_module_info;
local:
*;
};
diff --git a/test/.gitignore b/test/.gitignore
index 9f66dbd..20b56b7 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -1,2 +1,3 @@
.dirstamp
test-init
+test-loaded
diff --git a/test/test-loaded.c b/test/test-loaded.c
new file mode 100644
index 0000000..c81c288
--- /dev/null
+++ b/test/test-loaded.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <libkmod.h>
+
+
+int main(int argc, char *argv[])
+{
+ struct kmod_ctx *ctx;
+ struct kmod_loaded *mod;
+ struct kmod_list *list, *itr;
+ int err;
+
+ ctx = kmod_new();
+ if (ctx == NULL)
+ exit(EXIT_FAILURE);
+
+ printf("libkmod version %s\n", VERSION);
+
+ err = kmod_loaded_new(ctx, &mod);
+ if (err < 0)
+ exit(EXIT_FAILURE);
+
+ err = kmod_loaded_get_list(mod, &list);
+ if (err < 0) {
+ fprintf(stderr, "%s\n", strerror(-err));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Module Size Used by\n");
+
+ kmod_list_foreach(itr, list) {
+ const char *name, *deps;
+ long size;
+ int use_count;
+ kmod_loaded_get_module_info(itr, &name, &size, &use_count,
+ &deps, NULL);
+ printf("%-19s %8ld %d %s\n", name, size,
+ use_count, deps ? deps : "");
+ }
+
+ kmod_loaded_unref(mod);
+
+ kmod_unref(ctx);
+
+ return EXIT_SUCCESS;
+}