/* * 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(const 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; }