From: Gustavo Sverzut Barbieri Date: Sun, 4 Dec 2011 14:40:00 +0000 (-0200) Subject: kmod_module: extended information gathering. X-Git-Tag: v1~106 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f12ae3c438e845c0d895de99871db30e88baa1ca;p=thirdparty%2Fkmod.git 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 8bc644f7..10c8b165 100644 --- a/libkmod/libkmod-module.c +++ b/libkmod/libkmod-module.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -359,3 +361,239 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod, 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); + } +} diff --git a/libkmod/libkmod-private.h b/libkmod/libkmod-private.h index f00f3f9e..660a85c6 100644 --- a/libkmod/libkmod-private.h +++ b/libkmod/libkmod-private.h @@ -82,4 +82,9 @@ char *underscores(struct kmod_ctx *ctx, char *s) __attribute__((nonnull(1, 2))); bool startswith(const char *s, const char *prefix) __attribute__((nonnull(1, 2))); void *memdup(const void *p, size_t n) __attribute__((nonnull(1))); +ssize_t read_str_safe(int fd, char *buf, size_t buflen) __must_check __attribute__((nonnull(2))); +int read_str_long(int fd, long *value, int base) __must_check __attribute__((nonnull(2))); +int read_str_ulong(int fd, unsigned long *value, int base) __must_check __attribute__((nonnull(2))); + + #endif diff --git a/libkmod/libkmod-util.c b/libkmod/libkmod-util.c index b6e3cfc5..e506b52c 100644 --- a/libkmod/libkmod-util.c +++ b/libkmod/libkmod-util.c @@ -143,3 +143,64 @@ inline void *memdup(const void *p, size_t n) return memcpy(r, p, n); } + +ssize_t read_str_safe(int fd, char *buf, size_t buflen) { + size_t todo = buflen; + size_t done; + do { + ssize_t r = read(fd, buf, todo); + if (r == 0) + break; + else if (r > 0) + todo -= r; + else { + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINTR) + continue; + else + return -errno; + } + } while (todo > 0); + done = buflen - todo; + if (done == 0) + buf[0] = '\0'; + else { + if (done < buflen) + buf[done] = '\0'; + else if (buf[done - 1] != '\0') + return -ENOSPC; + } + return done; +} + +int read_str_long(int fd, long *value, int base) { + char buf[32], *end; + long v; + int err; + *value = 0; + err = read_str_safe(fd, buf, sizeof(buf)); + if (err < 0) + return err; + errno = 0; + v = strtol(buf, &end, base); + if (end == buf || !isspace(*end)) + return -EINVAL; + *value = v; + return 0; +} + +int read_str_ulong(int fd, unsigned long *value, int base) { + char buf[32], *end; + long v; + int err; + *value = 0; + err = read_str_safe(fd, buf, sizeof(buf)); + if (err < 0) + return err; + errno = 0; + v = strtoul(buf, &end, base); + if (end == buf || !isspace(*end)) + return -EINVAL; + *value = v; + return 0; +} diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h index fca41073..c552f290 100644 --- a/libkmod/libkmod.h +++ b/libkmod/libkmod.h @@ -117,6 +117,23 @@ int kmod_module_insert_module(struct kmod_module *mod, unsigned int flags); const char *kmod_module_get_name(const struct kmod_module *mod); const char *kmod_module_get_path(const struct kmod_module *mod); +enum kmod_module_initstate { + KMOD_MODULE_BUILTIN = 0, + KMOD_MODULE_LIVE, + KMOD_MODULE_COMING, + KMOD_MODULE_GOING +}; +const char *kmod_module_initstate_str(enum kmod_module_initstate initstate); +int kmod_module_get_initstate(const struct kmod_module *mod); +int kmod_module_get_refcnt(const struct kmod_module *mod); +struct kmod_list *kmod_module_get_holders(const struct kmod_module *mod); + +struct kmod_list *kmod_module_get_sections(const struct kmod_module *mod); +const char *kmod_module_section_get_name(const struct kmod_list *entry); +unsigned long kmod_module_section_get_address(const struct kmod_list *entry); +void kmod_module_section_free_list(struct kmod_list *list); + + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym index fcbe8a90..07690ddd 100644 --- a/libkmod/libkmod.sym +++ b/libkmod/libkmod.sym @@ -32,6 +32,16 @@ global: kmod_module_get_name; kmod_module_get_path; + + kmod_module_initstate_str; + kmod_module_get_initstate; + kmod_module_get_refcnt; + kmod_module_get_sections; + kmod_module_section_free_list; + kmod_module_section_get_name; + kmod_module_section_get_address; + + kmod_module_get_holders; local: *; };