kmod_module: extended information gathering.
provide means to get:
* refcount
* initstate
* holders
* sections
this can be used to individually query properties from modules,
similar to /proc/modules (kmod_loaded / kmod_loaded_module).
diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
index 8bc644f..10c8b16 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -27,6 +27,8 @@
#include <string.h>
#include <ctype.h>
#include <inttypes.h>
+#include <limits.h>
+#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
@@ -359,3 +361,239 @@
return err;
}
+
+KMOD_EXPORT const char *kmod_module_initstate_str(enum kmod_module_initstate state)
+{
+ switch (state) {
+ case KMOD_MODULE_BUILTIN:
+ return "builtin";
+ case KMOD_MODULE_LIVE:
+ return "live";
+ case KMOD_MODULE_COMING:
+ return "coming";
+ case KMOD_MODULE_GOING:
+ return "going";
+ default:
+ return NULL;
+ }
+}
+
+KMOD_EXPORT int kmod_module_get_initstate(const struct kmod_module *mod)
+{
+ char path[PATH_MAX], buf[32];
+ int fd, err, pathlen;
+
+ pathlen = snprintf(path, sizeof(path),
+ "/sys/module/%s/initstate", mod->name);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ err = -errno;
+
+ if (pathlen > (int)sizeof("/initstate") - 1) {
+ struct stat st;
+ path[pathlen - (sizeof("/initstate") - 1)] = '\0';
+ if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
+ return KMOD_MODULE_BUILTIN;
+ }
+
+ ERR(mod->ctx, "could not open '%s': %s\n",
+ path, strerror(-err));
+ return err;
+ }
+
+ err = read_str_safe(fd, buf, sizeof(buf));
+ close(fd);
+ if (err < 0) {
+ ERR(mod->ctx, "could not read from '%s': %s\n",
+ path, strerror(-err));
+ return err;
+ }
+
+ if (strcmp(buf, "live\n") == 0)
+ return KMOD_MODULE_LIVE;
+ else if (strcmp(buf, "coming\n") == 0)
+ return KMOD_MODULE_COMING;
+ else if (strcmp(buf, "going\n") == 0)
+ return KMOD_MODULE_GOING;
+
+ ERR(mod->ctx, "unknown %s: '%s'\n", path, buf);
+ return -EINVAL;
+}
+
+KMOD_EXPORT int kmod_module_get_refcnt(const struct kmod_module *mod)
+{
+ char path[PATH_MAX];
+ long refcnt;
+ int fd, err;
+
+ snprintf(path, sizeof(path), "/sys/module/%s/refcnt", mod->name);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ err = -errno;
+ ERR(mod->ctx, "could not open '%s': %s\n",
+ path, strerror(errno));
+ return err;
+ }
+
+ err = read_str_long(fd, &refcnt, 10);
+ close(fd);
+ if (err < 0) {
+ ERR(mod->ctx, "could not read integer from '%s': '%s'\n",
+ path, strerror(-err));
+ return err;
+ }
+
+ return (int)refcnt;
+}
+
+KMOD_EXPORT struct kmod_list *kmod_module_get_holders(const struct kmod_module *mod)
+{
+ char dname[PATH_MAX];
+ struct kmod_list *list = NULL;
+ DIR *d;
+ struct dirent *de;
+
+ if (mod == NULL)
+ return NULL;
+ snprintf(dname, sizeof(dname), "/sys/module/%s/holders", mod->name);
+
+ d = opendir(dname);
+ if (d == NULL) {
+ ERR(mod->ctx, "could not open '%s': %s\n",
+ dname, strerror(errno));
+ return NULL;
+ }
+
+ while ((de = readdir(d)) != NULL) {
+ struct kmod_list *node;
+ struct kmod_module *holder;
+ int err;
+
+ if (de->d_name[0] == '.') {
+ if (de->d_name[1] == '\0' ||
+ (de->d_name[1] == '.' && de->d_name[2] == '\0'))
+ continue;
+ }
+
+ err = kmod_module_new_from_name(mod->ctx, de->d_name, &holder);
+ if (err < 0) {
+ ERR(mod->ctx, "could not create module for '%s': %s\n",
+ de->d_name, strerror(-err));
+ continue;
+ }
+
+ node = kmod_list_append(list, holder);
+ if (node)
+ list = node;
+ else {
+ ERR(mod->ctx, "out of memory\n");
+ kmod_module_unref(holder);
+ }
+ }
+
+ closedir(d);
+ return list;
+}
+
+struct kmod_module_section {
+ unsigned long address;
+ char name[];
+};
+
+static void kmod_module_section_free(struct kmod_module_section *section)
+{
+ free(section);
+}
+
+KMOD_EXPORT struct kmod_list *kmod_module_get_sections(const struct kmod_module *mod)
+{
+ char dname[PATH_MAX];
+ struct kmod_list *list = NULL;
+ DIR *d;
+ int dfd;
+ struct dirent *de;
+
+ if (mod == NULL)
+ return NULL;
+ snprintf(dname, sizeof(dname), "/sys/module/%s/sections", mod->name);
+
+ d = opendir(dname);
+ if (d == NULL) {
+ ERR(mod->ctx, "could not open '%s': %s\n",
+ dname, strerror(errno));
+ return NULL;
+ }
+
+ dfd = dirfd(d);
+ while ((de = readdir(d)) != NULL) {
+ struct kmod_module_section *section;
+ struct kmod_list *node;
+ unsigned long address;
+ size_t namesz;
+ int fd, err;
+
+ if (de->d_name[0] == '.') {
+ if (de->d_name[1] == '\0' ||
+ (de->d_name[1] == '.' && de->d_name[2] == '\0'))
+ continue;
+ }
+
+ fd = openat(dfd, de->d_name, O_RDONLY);
+ if (fd < 0) {
+ ERR(mod->ctx, "could not open '%s/%s': %s\n",
+ dname, de->d_name, strerror(errno));
+ continue;
+ }
+
+ err = read_str_ulong(fd, &address, 16);
+ if (err < 0) {
+ ERR(mod->ctx, "could not read long from '%s/%s': %s\n",
+ dname, de->d_name, strerror(-err));
+ close(fd);
+ continue;
+ }
+
+ namesz = strlen(de->d_name) + 1;
+ section = malloc(sizeof(struct kmod_module_section) + namesz);
+ section->address = address;
+ memcpy(section->name, de->d_name, namesz);
+
+ node = kmod_list_append(list, section);
+ if (node)
+ list = node;
+ else {
+ ERR(mod->ctx, "out of memory\n");
+ free(section);
+ }
+ close(fd);
+ }
+
+ closedir(d);
+ return list;
+}
+
+KMOD_EXPORT const char *kmod_module_section_get_name(const struct kmod_list *entry)
+{
+ struct kmod_module_section *section;
+ if (entry == NULL)
+ return NULL;
+ section = entry->data;
+ return section->name;
+}
+
+KMOD_EXPORT unsigned long kmod_module_section_get_address(const struct kmod_list *entry)
+{
+ struct kmod_module_section *section;
+ if (entry == NULL)
+ return (unsigned long)-1;
+ section = entry->data;
+ return section->address;
+}
+
+KMOD_EXPORT void kmod_module_section_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_section_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}