From: Lucas De Marchi Date: Tue, 22 Nov 2011 07:42:09 +0000 (-0200) Subject: Add libkmod-loaded to handle live modules information X-Git-Tag: v1~206 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5369797d0903b5ebc8c616945e621fd56bf58a83;p=thirdparty%2Fkmod.git 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 57a1da3c..a6b81db8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,7 +31,8 @@ libkmod_libkmod_la_SOURCES =\ 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 @@ libkmod_libkmod_la_LDFLAGS = $(AM_LDFLAGS) \ 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 00000000..b0bed745 --- /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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 2f3006dd..93c5da19 100644 --- a/libkmod/libkmod.h +++ b/libkmod/libkmod.h @@ -22,6 +22,7 @@ #define _LIBABC_H_ #include +#include #ifdef __cplusplus extern "C" { @@ -64,4 +65,13 @@ struct kmod_list *kmod_list_next(struct kmod_list *first_entry, } /* 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 3a06d74e..0482adaf 100644 --- a/libkmod/libkmod.sym +++ b/libkmod/libkmod.sym @@ -10,6 +10,11 @@ global: 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 9f66dbd7..20b56b76 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 00000000..c81c2884 --- /dev/null +++ b/test/test-loaded.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +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; +}