| /* |
| * libkmod - interface to kernel module operations |
| * |
| * Copyright (C) 2011-2013 ProFUSION embedded systems |
| * |
| * 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; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * 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, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <assert.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <fnmatch.h> |
| #include <limits.h> |
| #include <stdarg.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/stat.h> |
| #include <sys/utsname.h> |
| |
| #include <shared/hash.h> |
| #include <shared/util.h> |
| |
| #include "libkmod.h" |
| #include "libkmod-internal.h" |
| #include "libkmod-index.h" |
| |
| #define KMOD_HASH_SIZE (256) |
| #define KMOD_LRU_MAX (128) |
| #define _KMOD_INDEX_MODULES_SIZE KMOD_INDEX_MODULES_BUILTIN + 1 |
| |
| /** |
| * SECTION:libkmod |
| * @short_description: libkmod context |
| * |
| * The context contains the default values for the library user, |
| * and is passed to all library operations. |
| */ |
| |
| static struct _index_files { |
| const char *fn; |
| const char *prefix; |
| } index_files[] = { |
| [KMOD_INDEX_MODULES_DEP] = { .fn = "modules.dep", .prefix = "" }, |
| [KMOD_INDEX_MODULES_ALIAS] = { .fn = "modules.alias", .prefix = "alias " }, |
| [KMOD_INDEX_MODULES_SYMBOL] = { .fn = "modules.symbols", .prefix = "alias "}, |
| [KMOD_INDEX_MODULES_BUILTIN] = { .fn = "modules.builtin", .prefix = ""}, |
| }; |
| |
| static const char *default_config_paths[] = { |
| SYSCONFDIR "/modprobe.d", |
| "/run/modprobe.d", |
| "/lib/modprobe.d", |
| NULL |
| }; |
| |
| /** |
| * kmod_ctx: |
| * |
| * Opaque object representing the library context. |
| */ |
| struct kmod_ctx { |
| int refcount; |
| int log_priority; |
| void (*log_fn)(void *data, |
| int priority, const char *file, int line, |
| const char *fn, const char *format, va_list args); |
| void *log_data; |
| const void *userdata; |
| char *dirname; |
| struct kmod_config *config; |
| struct hash *modules_by_name; |
| struct index_mm *indexes[_KMOD_INDEX_MODULES_SIZE]; |
| unsigned long long indexes_stamp[_KMOD_INDEX_MODULES_SIZE]; |
| }; |
| |
| void kmod_log(const struct kmod_ctx *ctx, |
| int priority, const char *file, int line, const char *fn, |
| const char *format, ...) |
| { |
| va_list args; |
| |
| if (ctx->log_fn == NULL) |
| return; |
| |
| va_start(args, format); |
| ctx->log_fn(ctx->log_data, priority, file, line, fn, format, args); |
| va_end(args); |
| } |
| |
| _printf_format_(6, 0) |
| static void log_filep(void *data, |
| int priority, const char *file, int line, |
| const char *fn, const char *format, va_list args) |
| { |
| FILE *fp = data; |
| #ifdef ENABLE_DEBUG |
| char buf[16]; |
| const char *priname; |
| switch (priority) { |
| case LOG_EMERG: |
| priname = "EMERGENCY"; |
| break; |
| case LOG_ALERT: |
| priname = "ALERT"; |
| break; |
| case LOG_CRIT: |
| priname = "CRITICAL"; |
| break; |
| case LOG_ERR: |
| priname = "ERROR"; |
| break; |
| case LOG_WARNING: |
| priname = "WARNING"; |
| break; |
| case LOG_NOTICE: |
| priname = "NOTICE"; |
| break; |
| case LOG_INFO: |
| priname = "INFO"; |
| break; |
| case LOG_DEBUG: |
| priname = "DEBUG"; |
| break; |
| default: |
| snprintf(buf, sizeof(buf), "L:%d", priority); |
| priname = buf; |
| } |
| fprintf(fp, "libkmod: %s %s:%d %s: ", priname, file, line, fn); |
| #else |
| fprintf(fp, "libkmod: %s: ", fn); |
| #endif |
| vfprintf(fp, format, args); |
| } |
| |
| const char *kmod_get_dirname(const struct kmod_ctx *ctx) |
| { |
| return ctx->dirname; |
| } |
| |
| /** |
| * kmod_get_userdata: |
| * @ctx: kmod library context |
| * |
| * Retrieve stored data pointer from library context. This might be useful |
| * to access from callbacks. |
| * |
| * Returns: stored userdata |
| */ |
| KMOD_EXPORT void *kmod_get_userdata(const struct kmod_ctx *ctx) |
| { |
| if (ctx == NULL) |
| return NULL; |
| return (void *)ctx->userdata; |
| } |
| |
| /** |
| * kmod_set_userdata: |
| * @ctx: kmod library context |
| * @userdata: data pointer |
| * |
| * Store custom @userdata in the library context. |
| */ |
| KMOD_EXPORT void kmod_set_userdata(struct kmod_ctx *ctx, const void *userdata) |
| { |
| if (ctx == NULL) |
| return; |
| ctx->userdata = userdata; |
| } |
| |
| static int log_priority(const char *priority) |
| { |
| char *endptr; |
| int prio; |
| |
| prio = strtol(priority, &endptr, 10); |
| if (endptr[0] == '\0' || isspace(endptr[0])) |
| return prio; |
| if (strncmp(priority, "err", 3) == 0) |
| return LOG_ERR; |
| if (strncmp(priority, "info", 4) == 0) |
| return LOG_INFO; |
| if (strncmp(priority, "debug", 5) == 0) |
| return LOG_DEBUG; |
| return 0; |
| } |
| |
| static const char *dirname_default_prefix = "/lib/modules"; |
| |
| static char *get_kernel_release(const char *dirname) |
| { |
| struct utsname u; |
| char *p; |
| |
| if (dirname != NULL) |
| return path_make_absolute_cwd(dirname); |
| |
| if (uname(&u) < 0) |
| return NULL; |
| |
| if (asprintf(&p, "%s/%s", dirname_default_prefix, u.release) < 0) |
| return NULL; |
| |
| return p; |
| } |
| |
| /** |
| * kmod_new: |
| * @dirname: what to consider as linux module's directory, if NULL |
| * defaults to /lib/modules/`uname -r`. If it's relative, |
| * it's treated as relative to the current working directory. |
| * Otherwise, give an absolute dirname. |
| * @config_paths: ordered array of paths (directories or files) where |
| * to load from user-defined configuration parameters such as |
| * alias, blacklists, commands (install, remove). If |
| * NULL defaults to /run/modprobe.d, /etc/modprobe.d and |
| * /lib/modprobe.d. Give an empty vector if configuration should |
| * not be read. This array must be null terminated. |
| * |
| * Create kmod library context. This reads the kmod configuration |
| * and fills in the default values. |
| * |
| * The initial refcount is 1, and needs to be decremented to |
| * release the resources of the kmod library context. |
| * |
| * Returns: a new kmod library context |
| */ |
| KMOD_EXPORT struct kmod_ctx *kmod_new(const char *dirname, |
| const char * const *config_paths) |
| { |
| const char *env; |
| struct kmod_ctx *ctx; |
| int err; |
| |
| ctx = calloc(1, sizeof(struct kmod_ctx)); |
| if (!ctx) |
| return NULL; |
| |
| ctx->refcount = 1; |
| ctx->log_fn = log_filep; |
| ctx->log_data = stderr; |
| ctx->log_priority = LOG_ERR; |
| |
| ctx->dirname = get_kernel_release(dirname); |
| |
| /* environment overwrites config */ |
| env = secure_getenv("KMOD_LOG"); |
| if (env != NULL) |
| kmod_set_log_priority(ctx, log_priority(env)); |
| |
| if (config_paths == NULL) |
| config_paths = default_config_paths; |
| err = kmod_config_new(ctx, &ctx->config, config_paths); |
| if (err < 0) { |
| ERR(ctx, "could not create config\n"); |
| goto fail; |
| } |
| |
| ctx->modules_by_name = hash_new(KMOD_HASH_SIZE, NULL); |
| if (ctx->modules_by_name == NULL) { |
| ERR(ctx, "could not create by-name hash\n"); |
| goto fail; |
| } |
| |
| INFO(ctx, "ctx %p created\n", ctx); |
| DBG(ctx, "log_priority=%d\n", ctx->log_priority); |
| |
| return ctx; |
| |
| fail: |
| free(ctx->modules_by_name); |
| free(ctx->dirname); |
| free(ctx); |
| return NULL; |
| } |
| |
| /** |
| * kmod_ref: |
| * @ctx: kmod library context |
| * |
| * Take a reference of the kmod library context. |
| * |
| * Returns: the passed kmod library context |
| */ |
| KMOD_EXPORT struct kmod_ctx *kmod_ref(struct kmod_ctx *ctx) |
| { |
| if (ctx == NULL) |
| return NULL; |
| ctx->refcount++; |
| return ctx; |
| } |
| |
| /** |
| * kmod_unref: |
| * @ctx: kmod library context |
| * |
| * Drop a reference of the kmod library context. If the refcount |
| * reaches zero, the resources of the context will be released. |
| * |
| * Returns: the passed kmod library context or NULL if it's freed |
| */ |
| KMOD_EXPORT struct kmod_ctx *kmod_unref(struct kmod_ctx *ctx) |
| { |
| if (ctx == NULL) |
| return NULL; |
| |
| if (--ctx->refcount > 0) |
| return ctx; |
| |
| INFO(ctx, "context %p released\n", ctx); |
| |
| kmod_unload_resources(ctx); |
| hash_free(ctx->modules_by_name); |
| free(ctx->dirname); |
| if (ctx->config) |
| kmod_config_free(ctx->config); |
| |
| free(ctx); |
| return NULL; |
| } |
| |
| /** |
| * kmod_set_log_fn: |
| * @ctx: kmod library context |
| * @log_fn: function to be called for logging messages |
| * @data: data to pass to log function |
| * |
| * The built-in logging writes to stderr. It can be |
| * overridden by a custom function, to plug log messages |
| * into the user's logging functionality. |
| */ |
| KMOD_EXPORT void kmod_set_log_fn(struct kmod_ctx *ctx, |
| void (*log_fn)(void *data, |
| int priority, const char *file, |
| int line, const char *fn, |
| const char *format, va_list args), |
| const void *data) |
| { |
| if (ctx == NULL) |
| return; |
| ctx->log_fn = log_fn; |
| ctx->log_data = (void *)data; |
| INFO(ctx, "custom logging function %p registered\n", log_fn); |
| } |
| |
| /** |
| * kmod_get_log_priority: |
| * @ctx: kmod library context |
| * |
| * Returns: the current logging priority |
| */ |
| KMOD_EXPORT int kmod_get_log_priority(const struct kmod_ctx *ctx) |
| { |
| if (ctx == NULL) |
| return -1; |
| return ctx->log_priority; |
| } |
| |
| /** |
| * kmod_set_log_priority: |
| * @ctx: kmod library context |
| * @priority: the new logging priority |
| * |
| * Set the current logging priority. The value controls which messages |
| * are logged. |
| */ |
| KMOD_EXPORT void kmod_set_log_priority(struct kmod_ctx *ctx, int priority) |
| { |
| if (ctx == NULL) |
| return; |
| ctx->log_priority = priority; |
| } |
| |
| struct kmod_module *kmod_pool_get_module(struct kmod_ctx *ctx, |
| const char *key) |
| { |
| struct kmod_module *mod; |
| |
| mod = hash_find(ctx->modules_by_name, key); |
| |
| DBG(ctx, "get module name='%s' found=%p\n", key, mod); |
| |
| return mod; |
| } |
| |
| void kmod_pool_add_module(struct kmod_ctx *ctx, struct kmod_module *mod, |
| const char *key) |
| { |
| DBG(ctx, "add %p key='%s'\n", mod, key); |
| |
| hash_add(ctx->modules_by_name, key, mod); |
| } |
| |
| void kmod_pool_del_module(struct kmod_ctx *ctx, struct kmod_module *mod, |
| const char *key) |
| { |
| DBG(ctx, "del %p key='%s'\n", mod, key); |
| |
| hash_del(ctx->modules_by_name, key); |
| } |
| |
| static int kmod_lookup_alias_from_alias_bin(struct kmod_ctx *ctx, |
| enum kmod_index index_number, |
| const char *name, |
| struct kmod_list **list) |
| { |
| int err, nmatch = 0; |
| struct index_file *idx; |
| struct index_value *realnames, *realname; |
| |
| if (ctx->indexes[index_number] != NULL) { |
| DBG(ctx, "use mmaped index '%s' for name=%s\n", |
| index_files[index_number].fn, name); |
| realnames = index_mm_searchwild(ctx->indexes[index_number], |
| name); |
| } else { |
| char fn[PATH_MAX]; |
| |
| snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, |
| index_files[index_number].fn); |
| |
| DBG(ctx, "file=%s name=%s\n", fn, name); |
| |
| idx = index_file_open(fn); |
| if (idx == NULL) |
| return -ENOSYS; |
| |
| realnames = index_searchwild(idx, name); |
| index_file_close(idx); |
| } |
| |
| for (realname = realnames; realname; realname = realname->next) { |
| struct kmod_module *mod; |
| |
| err = kmod_module_new_from_alias(ctx, name, realname->value, &mod); |
| if (err < 0) { |
| ERR(ctx, "Could not create module for alias=%s realname=%s: %s\n", |
| name, realname->value, strerror(-err)); |
| goto fail; |
| } |
| |
| *list = kmod_list_append(*list, mod); |
| nmatch++; |
| } |
| |
| index_values_free(realnames); |
| return nmatch; |
| |
| fail: |
| *list = kmod_list_remove_n_latest(*list, nmatch); |
| index_values_free(realnames); |
| return err; |
| |
| } |
| |
| int kmod_lookup_alias_from_symbols_file(struct kmod_ctx *ctx, const char *name, |
| struct kmod_list **list) |
| { |
| if (!strstartswith(name, "symbol:")) |
| return 0; |
| |
| return kmod_lookup_alias_from_alias_bin(ctx, KMOD_INDEX_MODULES_SYMBOL, |
| name, list); |
| } |
| |
| int kmod_lookup_alias_from_aliases_file(struct kmod_ctx *ctx, const char *name, |
| struct kmod_list **list) |
| { |
| return kmod_lookup_alias_from_alias_bin(ctx, KMOD_INDEX_MODULES_ALIAS, |
| name, list); |
| } |
| |
| int kmod_lookup_alias_from_builtin_file(struct kmod_ctx *ctx, const char *name, |
| struct kmod_list **list) |
| { |
| char *line = NULL; |
| int err = 0; |
| |
| assert(*list == NULL); |
| |
| if (ctx->indexes[KMOD_INDEX_MODULES_BUILTIN]) { |
| DBG(ctx, "use mmaped index '%s' modname=%s\n", |
| index_files[KMOD_INDEX_MODULES_BUILTIN].fn, |
| name); |
| line = index_mm_search(ctx->indexes[KMOD_INDEX_MODULES_BUILTIN], |
| name); |
| } else { |
| struct index_file *idx; |
| char fn[PATH_MAX]; |
| |
| snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, |
| index_files[KMOD_INDEX_MODULES_BUILTIN].fn); |
| DBG(ctx, "file=%s modname=%s\n", fn, name); |
| |
| idx = index_file_open(fn); |
| if (idx == NULL) { |
| DBG(ctx, "could not open builtin file '%s'\n", fn); |
| goto finish; |
| } |
| |
| line = index_search(idx, name); |
| index_file_close(idx); |
| } |
| |
| if (line != NULL) { |
| struct kmod_module *mod; |
| |
| err = kmod_module_new_from_name(ctx, name, &mod); |
| if (err < 0) { |
| ERR(ctx, "Could not create module from name %s: %s\n", |
| name, strerror(-err)); |
| goto finish; |
| } |
| |
| kmod_module_set_builtin(mod, true); |
| *list = kmod_list_append(*list, mod); |
| if (*list == NULL) |
| err = -ENOMEM; |
| } |
| |
| finish: |
| free(line); |
| return err; |
| } |
| |
| char *kmod_search_moddep(struct kmod_ctx *ctx, const char *name) |
| { |
| struct index_file *idx; |
| char fn[PATH_MAX]; |
| char *line; |
| |
| if (ctx->indexes[KMOD_INDEX_MODULES_DEP]) { |
| DBG(ctx, "use mmaped index '%s' modname=%s\n", |
| index_files[KMOD_INDEX_MODULES_DEP].fn, name); |
| return index_mm_search(ctx->indexes[KMOD_INDEX_MODULES_DEP], |
| name); |
| } |
| |
| snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, |
| index_files[KMOD_INDEX_MODULES_DEP].fn); |
| |
| DBG(ctx, "file=%s modname=%s\n", fn, name); |
| |
| idx = index_file_open(fn); |
| if (idx == NULL) { |
| DBG(ctx, "could not open moddep file '%s'\n", fn); |
| return NULL; |
| } |
| |
| line = index_search(idx, name); |
| index_file_close(idx); |
| |
| return line; |
| } |
| |
| int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name, |
| struct kmod_list **list) |
| { |
| char *line; |
| int n = 0; |
| |
| /* |
| * Module names do not contain ':'. Return early if we know it will |
| * not be found. |
| */ |
| if (strchr(name, ':')) |
| return 0; |
| |
| line = kmod_search_moddep(ctx, name); |
| if (line != NULL) { |
| struct kmod_module *mod; |
| |
| n = kmod_module_new_from_name(ctx, name, &mod); |
| if (n < 0) { |
| ERR(ctx, "Could not create module from name %s: %s\n", |
| name, strerror(-n)); |
| goto finish; |
| } |
| |
| *list = kmod_list_append(*list, mod); |
| kmod_module_parse_depline(mod, line); |
| } |
| |
| finish: |
| free(line); |
| |
| return n; |
| } |
| |
| int kmod_lookup_alias_from_config(struct kmod_ctx *ctx, const char *name, |
| struct kmod_list **list) |
| { |
| struct kmod_config *config = ctx->config; |
| struct kmod_list *l; |
| int err, nmatch = 0; |
| |
| kmod_list_foreach(l, config->aliases) { |
| const char *aliasname = kmod_alias_get_name(l); |
| const char *modname = kmod_alias_get_modname(l); |
| |
| if (fnmatch(aliasname, name, 0) == 0) { |
| struct kmod_module *mod; |
| |
| err = kmod_module_new_from_alias(ctx, aliasname, |
| modname, &mod); |
| if (err < 0) { |
| ERR(ctx, "Could not create module for alias=%s modname=%s: %s\n", |
| name, modname, strerror(-err)); |
| goto fail; |
| } |
| |
| *list = kmod_list_append(*list, mod); |
| nmatch++; |
| } |
| } |
| |
| return nmatch; |
| |
| fail: |
| *list = kmod_list_remove_n_latest(*list, nmatch); |
| return err; |
| } |
| |
| int kmod_lookup_alias_from_commands(struct kmod_ctx *ctx, const char *name, |
| struct kmod_list **list) |
| { |
| struct kmod_config *config = ctx->config; |
| struct kmod_list *l, *node; |
| int err, nmatch = 0; |
| |
| kmod_list_foreach(l, config->install_commands) { |
| const char *modname = kmod_command_get_modname(l); |
| |
| if (streq(modname, name)) { |
| const char *cmd = kmod_command_get_command(l); |
| struct kmod_module *mod; |
| |
| err = kmod_module_new_from_name(ctx, modname, &mod); |
| if (err < 0) { |
| ERR(ctx, "Could not create module from name %s: %s\n", |
| modname, strerror(-err)); |
| return err; |
| } |
| |
| node = kmod_list_append(*list, mod); |
| if (node == NULL) { |
| ERR(ctx, "out of memory\n"); |
| return -ENOMEM; |
| } |
| |
| *list = node; |
| nmatch = 1; |
| |
| kmod_module_set_install_commands(mod, cmd); |
| |
| /* |
| * match only the first one, like modprobe from |
| * module-init-tools does |
| */ |
| break; |
| } |
| } |
| |
| if (nmatch) |
| return nmatch; |
| |
| kmod_list_foreach(l, config->remove_commands) { |
| const char *modname = kmod_command_get_modname(l); |
| |
| if (streq(modname, name)) { |
| const char *cmd = kmod_command_get_command(l); |
| struct kmod_module *mod; |
| |
| err = kmod_module_new_from_name(ctx, modname, &mod); |
| if (err < 0) { |
| ERR(ctx, "Could not create module from name %s: %s\n", |
| modname, strerror(-err)); |
| return err; |
| } |
| |
| node = kmod_list_append(*list, mod); |
| if (node == NULL) { |
| ERR(ctx, "out of memory\n"); |
| return -ENOMEM; |
| } |
| |
| *list = node; |
| nmatch = 1; |
| |
| kmod_module_set_remove_commands(mod, cmd); |
| |
| /* |
| * match only the first one, like modprobe from |
| * module-init-tools does |
| */ |
| break; |
| } |
| } |
| |
| return nmatch; |
| } |
| |
| void kmod_set_modules_visited(struct kmod_ctx *ctx, bool visited) |
| { |
| struct hash_iter iter; |
| const void *v; |
| |
| hash_iter_init(ctx->modules_by_name, &iter); |
| while (hash_iter_next(&iter, NULL, &v)) |
| kmod_module_set_visited((struct kmod_module *)v, visited); |
| } |
| |
| void kmod_set_modules_required(struct kmod_ctx *ctx, bool required) |
| { |
| struct hash_iter iter; |
| const void *v; |
| |
| hash_iter_init(ctx->modules_by_name, &iter); |
| while (hash_iter_next(&iter, NULL, &v)) |
| kmod_module_set_required((struct kmod_module *)v, required); |
| } |
| |
| static bool is_cache_invalid(const char *path, unsigned long long stamp) |
| { |
| struct stat st; |
| |
| if (stat(path, &st) < 0) |
| return true; |
| |
| if (stamp != stat_mstamp(&st)) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * kmod_validate_resources: |
| * @ctx: kmod library context |
| * |
| * Check if indexes and configuration files changed on disk and the current |
| * context is not valid anymore. |
| * |
| * Returns: KMOD_RESOURCES_OK if resources are still valid, |
| * KMOD_RESOURCES_MUST_RELOAD if it's sufficient to call |
| * kmod_unload_resources() and kmod_load_resources() or |
| * KMOD_RESOURCES_MUST_RECREATE if @ctx must be re-created. |
| */ |
| KMOD_EXPORT int kmod_validate_resources(struct kmod_ctx *ctx) |
| { |
| struct kmod_list *l; |
| size_t i; |
| |
| if (ctx == NULL || ctx->config == NULL) |
| return KMOD_RESOURCES_MUST_RECREATE; |
| |
| kmod_list_foreach(l, ctx->config->paths) { |
| struct kmod_config_path *cf = l->data; |
| |
| if (is_cache_invalid(cf->path, cf->stamp)) |
| return KMOD_RESOURCES_MUST_RECREATE; |
| } |
| |
| for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) { |
| char path[PATH_MAX]; |
| |
| if (ctx->indexes[i] == NULL) |
| continue; |
| |
| snprintf(path, sizeof(path), "%s/%s.bin", ctx->dirname, |
| index_files[i].fn); |
| |
| if (is_cache_invalid(path, ctx->indexes_stamp[i])) |
| return KMOD_RESOURCES_MUST_RELOAD; |
| } |
| |
| return KMOD_RESOURCES_OK; |
| } |
| |
| /** |
| * kmod_load_resources: |
| * @ctx: kmod library context |
| * |
| * Load indexes and keep them open in @ctx. This way it's faster to lookup |
| * information within the indexes. If this function is not called before a |
| * search, the necessary index is always opened and closed. |
| * |
| * If user will do more than one or two lookups, insertions, deletions, most |
| * likely it's good to call this function first. Particularly in a daemon like |
| * udev that on bootup issues hundreds of calls to lookup the index, calling |
| * this function will speedup the searches. |
| * |
| * Returns: 0 on success or < 0 otherwise. |
| */ |
| KMOD_EXPORT int kmod_load_resources(struct kmod_ctx *ctx) |
| { |
| size_t i; |
| |
| if (ctx == NULL) |
| return -ENOENT; |
| |
| for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) { |
| char path[PATH_MAX]; |
| |
| if (ctx->indexes[i] != NULL) { |
| INFO(ctx, "Index %s already loaded\n", |
| index_files[i].fn); |
| continue; |
| } |
| |
| snprintf(path, sizeof(path), "%s/%s.bin", ctx->dirname, |
| index_files[i].fn); |
| ctx->indexes[i] = index_mm_open(ctx, path, |
| &ctx->indexes_stamp[i]); |
| if (ctx->indexes[i] == NULL) |
| goto fail; |
| } |
| |
| return 0; |
| |
| fail: |
| kmod_unload_resources(ctx); |
| return -ENOMEM; |
| } |
| |
| /** |
| * kmod_unload_resources: |
| * @ctx: kmod library context |
| * |
| * Unload all the indexes. This will free the resources to maintain the index |
| * open and all subsequent searches will need to open and close the index. |
| * |
| * User is free to call kmod_load_resources() and kmod_unload_resources() as |
| * many times as wanted during the lifecycle of @ctx. For example, if a daemon |
| * knows that when starting up it will lookup a lot of modules, it could call |
| * kmod_load_resources() and after the first burst of searches is gone, it |
| * could free the resources by calling kmod_unload_resources(). |
| * |
| * Returns: 0 on success or < 0 otherwise. |
| */ |
| KMOD_EXPORT void kmod_unload_resources(struct kmod_ctx *ctx) |
| { |
| size_t i; |
| |
| if (ctx == NULL) |
| return; |
| |
| for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) { |
| if (ctx->indexes[i] != NULL) { |
| index_mm_close(ctx->indexes[i]); |
| ctx->indexes[i] = NULL; |
| ctx->indexes_stamp[i] = 0; |
| } |
| } |
| } |
| |
| /** |
| * kmod_dump_index: |
| * @ctx: kmod library context |
| * @type: index to dump, valid indexes are |
| * KMOD_INDEX_MODULES_DEP: index of module dependencies; |
| * KMOD_INDEX_MODULES_ALIAS: index of module aliases; |
| * KMOD_INDEX_MODULES_SYMBOL: index of symbol aliases; |
| * KMOD_INDEX_MODULES_BUILTIN: index of builtin module. |
| * @fd: file descriptor to dump index to |
| * |
| * Dump index to file descriptor. Note that this function doesn't use stdio.h |
| * so call fflush() before calling this function to be sure data is written in |
| * order. |
| * |
| * Returns: 0 on success or < 0 otherwise. |
| */ |
| KMOD_EXPORT int kmod_dump_index(struct kmod_ctx *ctx, enum kmod_index type, |
| int fd) |
| { |
| if (ctx == NULL) |
| return -ENOSYS; |
| |
| if (type < 0 || type >= _KMOD_INDEX_MODULES_SIZE) |
| return -ENOENT; |
| |
| if (ctx->indexes[type] != NULL) { |
| DBG(ctx, "use mmaped index '%s'\n", index_files[type].fn); |
| index_mm_dump(ctx->indexes[type], fd, |
| index_files[type].prefix); |
| } else { |
| char fn[PATH_MAX]; |
| struct index_file *idx; |
| |
| snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, |
| index_files[type].fn); |
| |
| DBG(ctx, "file=%s\n", fn); |
| |
| idx = index_file_open(fn); |
| if (idx == NULL) |
| return -ENOSYS; |
| |
| index_dump(idx, fd, index_files[type].prefix); |
| index_file_close(idx); |
| } |
| |
| return 0; |
| } |
| |
| const struct kmod_config *kmod_get_config(const struct kmod_ctx *ctx) |
| { |
| return ctx->config; |
| } |